Swift: 符合协议声明 class 函数返回实例

Swift: Conform to protocol declaring class function returning instance

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

在 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: ...)
    }
}

但是无论我如何配置它都会抛出错误。

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

你的问题是因为 return 从 randomExample 变成 UIColor,因为 randomExample 希望你 return 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 将尝试 return 一个 UIColor。但是,randomExample 期望 return 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 Self and

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()