【问题标题】:Swift: Conform to protocol declaring class function returning instanceSwift:符合协议声明类函数返回实例
【发布时间】:2015-09-08 18:11:39
【问题描述】:

我想要一个通用协议来返回给定类的新“随机”配置实例。

在 ObjC 中:

@protocol Random
+ (instancetype)random;
@end

@interface UIColor (Random)
<Random>
@end

@implementation
+ (instancetype)random {
    return [UIColor colorWith...];
}
@end

它在 ObjC 中工作,但我无法让它在 Swift 中工作。

在 Swift 中:

protocol Random {
    static func randomExample() -> Self
}

extension UIColor: Random {
    final class func randomExample() -> UIColor {
        return UIColor(red: ...)
    }
}

但是无论我如何配置它都会引发错误。

如何正确地为返回符合类实例的类方法制定协议?

【问题讨论】:

  • 它会抛出什么错误?

标签: ios swift protocols


【解决方案1】:

您的问题源于尝试从randomExample 返回UIColor,因为randomExample 希望您返回Self

举以下不正确的例子:

// Here `Self` is `UIColor`.
extension UIColor: Random {
    class func randomExample() -> Self { 
        return UIColor(...) // Here's the problem...
    }
}

// Here `Self` is `MyColor`.
class MyColor: UIColor {}

由于randomExample 没有被MyColor 覆盖,所以MyColor 调用的randomExample 将尝试返回UIColor。但是,randomExample 期望返回 MyColor 实例。

要解决这个问题,你可以这样做:

extension UIColor: Random {
    class func randomExample() -> Self {
        // `self` refers to the current class.
        // If this wasn't a class method you would use `self.dynamicType`
        return self(red: 0, green: 0, blue: 0, alpha: 0)
    }
}

let color1 = UIColor.randomExample() // color1 is a `UIColor`.
let color2 = MyColor.randomExample() // color2 is a `MyColor`.

如果你使用的是 Swift 2,你需要使用:

self.init(red: 0, green: 0, blue: 0, alpha: 0)

您可能还感兴趣:Protocol func returning SelfImplementing NSCopying in Swift with subclasses

【讨论】:

  • 这里的魔力确实在self()self.init() 中——我从未猜到这一点,也无法使用谷歌搜索找到这个提示。谢谢。
【解决方案2】:

ABakerSmith 回答您的问题值得称赞,但我想扩展他的回答,以展示相同的协议如何适用于 Struct 和 Enum 的值类型。由于无法派生值类型,因此它们的协议实现仅使用类型名称而不是 Self。

编辑:按要求添加代码。

protocol Random {
    static func random() -> Self
}

extension Float: Random {
    static func random() -> Float {
        return Float(arc4random()) / Float(UInt32.max)
    }
}

extension CGFloat: Random {
    static func random() -> CGFloat {
        return CGFloat(Float.random())
    }
}

extension UIColor: Random {
    static func random() -> Self {
        return self.init(
            red:   CGFloat.random(),
            green: CGFloat.random(),
            blue:  CGFloat.random(),
            alpha: 1.0)
    }
}

let c1 = UIColor.random()
let c2 = UIColor.random()
let c3 = UIColor.random()

【讨论】:

  • 就我个人而言,我更喜欢您的回答,并且发现它更有帮助(除了我无法复制粘贴代码) - 是的,从理解的角度来看,不仅仅是完成的角度。尽管如此,我还是尊重了你的要求。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多