Swift,从AnyObject中的Self

3

能否从 AnyObject 中获取 Self?

看这个例子:

// Superclass
class ManagedObject {
  class func findByID(id: String) -> AnyObject? {
    let objects = objectsWithPredicate(NSPredicate(format: "id == %@", id))
    return objects.firstObject() // Returns AnyObject
  }
}

// Subclass
class User : ManagedObject {
  class func returnFirstSelf() -> Self? {
    return findById("1") // This doesn't work because it returns AnyObject, but I need Self.
  }
}

如果不行,最好的替代方法是什么?如何确保在调用User.returnFirstSelf()时,编译器返回User,而在调用UserSubclass.returnFirstSelf()时,它返回UserSubclass

1
很遗憾,"'Self'只能在协议中使用或作为类方法的结果;你是不是想用'ManagedObject'?" - Baub
@BryanChen 这导致另一个错误,"'User' 无法转换为 'Self'"。 - Craig Otis
最佳实现方式是什么?你能更不正式地解释一下你想做什么吗? - matt
2
我认为 OP 只是想确保在调用 User.returnFirstSelf() 时,编译器尝试返回一个 User。而在调用 SomeUserSubclass.returnFirstSelf() 时,它会返回一个 SomeUserSubclass - Craig Otis
@CraigOtis 没错! - Baub
2个回答

4

如果可以的话,您可以从类函数中简单地返回 User?:

public class func returnFirstSelf() -> User? {
    if let found = findByID("1") as? User {
        return found
    }
    return nil
}

目前我所知道的,Swift 没有办法返回 Self?。问题在于,Self 具有一定的动态含义,与具体类型、协议和甚至泛型不同。一个典型的例子是:如果你有一个继承自 UserStudentUser 类,那么如果你尝试像这样实现:

class func returnFirstSelf() -> Self? {
    if let found = findById("1") as? Self { // Note the check that 'found' is a 'Self'
        return found
    }
    return nil
}

然后你会遇到编译器错误,因为你不能在协议或类方法之外使用Self。如果你尝试像这样实现:

class func returnFirstSelf() -> Self? {
    if let found = findById("1") as? User { // Note 'User' instead of 'Self'
        return found
    }
    return nil
}

然后你会有风险,即Self实际上意味着StudentUser,即使你通过了需要foundUser的检查,也不能保证foundStudentUser。这将发生在StudentUser没有覆盖方法以确保as?检查针对StudentUser的情况下。
我认为这里的关键缺陷是你无法在类方法上使用required关键字,要求子类覆盖它们。这将允许编译器确保任何子类已经重写该方法并提供了一个可以保证类型安全的实现。

我正在通过心灵感应与 @RobNapier 进行ping操作。 - Craig Otis
谢谢Craig。如果我创建ManagedObject的子类叫做Dog,这意味着我必须再次在Dog下重写returnFirstSelf()来完成完全相同的任务,但是特别地返回一个Dog而不是其他。这就是我想避免的。 - Baub
1
你说得对。不过,目前来看,这是一个选项。另一个选项是从你的类函数中简单地返回 AnyObject?,并在使用返回值检查它们是否符合你期望的 Dog 类型时使用 if let 表达式。 - Craig Otis

0
Craig Otis的回答非常准确。然而,一个选项是创建一个复制构造函数。这可能不适用于提问者从NSManagedObject继承的情况,但对于非托管对象应该可以工作。
required init(user: User) {
    super.init(user: User) // or super.init() if top of inheritance.
    self.name = user.name
    self.email = user.email
    // etc.
}

class func returnFirstSelf() -> Self? {
    if let found = findById("1") { // Note the check that 'found' is a 'Self'
        return self.init(copy: found as! User)
    }
    return nil
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接