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

时间:2015-09-08 18:11:40

标签: ios swift protocols

我希望有一个通用的协议来返回给定类的新的"随意配置的实例。

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

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

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

2 个答案:

答案 0 :(得分:4)

您的问题源于尝试从UIColor返回randomExample,因为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未覆盖MyColorrandomExample调用的MyColor会尝试返回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

答案 1 :(得分:4)

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

enter image description here

编辑:按要求添加代码。

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