【问题标题】:Invoking Swift Protocol Initializers调用 Swift 协议初始化器
【发布时间】:2020-05-17 14:20:56
【问题描述】:

Swift 5,在 XCode 11.2.1 上。我一直在用 Java 编程,在这一点上对 Swift 的熟练程度只是中等,所以我的一些理解被 Java 习语所影响。

假设我们提供了一个协议,或者可能是一个伪抽象类,它可以在一个单独的模块中实现。然后,程序员将对他们的类的引用传递回第一个模块中的代码,该模块实例化他们的类。现在,在 Java 中这很困难,因为您无法保证子类将定义哪些初始化程序/静态方法。但是,在 Swift 中,您可以。这很整洁。除了,我还没有找到使用它们的方法。我期望会是你可以有像例如这样的代码

protocol FooP {
  init(s: String)
}

func instantiate(clazz: FooP.Type) -> FooP {
  return clazz.init(s: "qwerty")
}

...OOOOORRRR 确实有效,正如我所希望的那样。我认为在我的测试中,我只是未能找到 CLASS/.Type/.self 的正确组合来提示我成功的可能性。

由于我没有找到有关这种可能性的任何其他信息,尽管搜索了 30-60 分钟,我还是会回答我自己的问题。

【问题讨论】:

    标签: ios swift types static swift-protocols


    【解决方案1】:

    事实证明,您可以使用协议提供的静态方法,即使涉及的类(不是其实例化)被强制转换为它们的协议。您甚至可以使用泛型使示例instantiate 方法更方便地具有您为其提供的类型的返回类型,尽管这会导致不同的问题,如下所示。这里有一堆代码展示了你能做和不能做的事情:

    public protocol ProtocolTest {
        init(s: String)
    
        static func factory(s: String) -> Self
    }
    
    public class Foo : ProtocolTest, CustomStringConvertible {
        public let s: String
    
        public required init(s: String) {
            self.s = s
        }
    
        public class func factory(s: String) -> Self {
            return Self(s: s)
        }
    
        public var description: String { return "{\(Self.self)|s:\"\(s)\"}" }
    }
    
    public class Bar : Foo {
    }
    
    public func instantiateGeneric<T : ProtocolTest>(clazz: T.Type) -> T {
        return clazz.init(s: "qwertyGeneric")
    }
    
    public func instantiateNongeneric(clazz: ProtocolTest.Type) -> ProtocolTest {
        return clazz.init(s: "qwertyNongeneric")
    }
    
    public func run() {
        let ptts: [ProtocolTest.Type] = [Foo.self, Bar.self]
    
        // Success
        let aInit : Bar = Bar.init(s: "qwertyInit")
        // Success
        let aGeneric : Bar = instantiateGeneric(clazz: Bar.self)
        // Compile error: Cannot convert value of type 'ProtocolTest' to specified type 'Bar'
        let aNongeneric : Bar = instantiateNongeneric(clazz: Bar.self)
    
        for ptt in ptts {
            // Success
            let bInit : ProtocolTest = ptt.init(s: "qwertyInit")
            // Compile error: Protocol type 'ProtocolTest' cannot conform to 'ProtocolTest' because only concrete types can conform to protocols
            let bGeneric1 : ProtocolTest = instantiateGeneric(clazz: ptt)
            // Compile error: Cannot invoke 'instantiateGeneric' with an argument list of type '(clazz: ProtocolTest.Type)'
            let bGeneric2 = instantiateGeneric(clazz: ptt)
            // Success
            let bNongeneric : ProtocolTest = instantiateNongeneric(clazz: ptt)
            // This works too, btw:
            let bFactory : ProtocolTest = ptt.factory(s: "qwertyFactory")
        }
    }
    

    我不确定循环中的 instantiateGeneric 到底是怎么回事。这似乎是一个合理的代码行。如果您有解释,请告诉我。也许关于&lt;T : ProtocolTest&gt;ProtocolTest 在技术上不符合ProtocolTest,或者它必须是一个严格的子类?不确定。能够对两种用途使用相同的方法会很好,但我已经对我能做的事情感到非常满意。这个发现可能足以让我真正喜欢斯威夫特,哈哈。

    【讨论】:

    • 有趣的事实:在一个类中,static func 等价于final class func
    • 另外一个有趣的事实:似乎当你试图对你的类型产生超出我上面展示的类型时,Swift 会立即对你进行打击。例如,显然你不能在不指定类型参数的情况下使用泛型类。而且没有任何通配符。
    猜你喜欢
    • 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
    相关资源
    最近更新 更多