【问题标题】:What's the Swift equivalent of declaring `typedef SomeClass<SomeProtocol> MyType`?声明 `typedef SomeClass<SomeProtocol> MyType` 的 Swift 等价物是什么?
【发布时间】:2014-10-20 20:11:16
【问题描述】:

我目前正在一个主要是 Objective-C 的项目中编写一些 Swift 代码。在我们的 ObjC 代码中,我们有一个声明 typedef GPUImageOutput&lt;GPUImageInput&gt; MyFilter; 的标头。然后我们可以声明例如@property 只能是实现GPUImageInputGPUImageOutput 子类。

(注意:GPUImageOutputGPUImageInput 不是我定义的;它们是 GPUImage library 的一部分)

我们的 Swift 代码似乎无法识别这一点,即使标题在我们的桥接头中是 #imported 的。我尝试在 Swift 中复制声明,但这些都不是正确的语法:
typealias MyFilter = GPUImageOutput, GPUImageInput
typealias MyFilter = GPUImageOutput : GPUImageInput

【问题讨论】:

标签: ios objective-c swift gpuimage


【解决方案1】:

你不能这样声明类型别名。

我们能做的最好的事情是这样的:

class MyClass {
    private var filter:GPUImageOutput

    init<FilterType:GPUImageOutput where FilterType:GPUImageInput>(filter:FilterType) {
        self.filter = filter
    }

    func setFilter<FilterType:GPUImageOutput where FilterType:GPUImageInput>(filter:FilterType) {
        self.filter = filter
    }

    func someMethod() {
        let output = self.filter
        let input = self.filter as GPUImageInput
        output.someOutputMethod()
        input.someInputMethod()
    }
}

【讨论】:

【解决方案2】:

在 Swift 4 中,您可以使用新的 & 符号来实现这一点(以下是确认 UIViewController 和 UITableViewDataSource 的参数示例:

func foo(vc: UIViewController & UITableViewDataSource) {
    // access UIViewController property
    let view = vc.view
    // call UITableViewDataSource method
    let sections = vc.numberOfSectionsInTableView?(tableView)
}

【讨论】:

    【解决方案3】:

    在 Swift 中,类似以下内容应该可以完成您的任务,但它与对应的 ObjC 不同:

    typealias GPUImageOutput = UIImage
    @objc protocol GPUImageInput {
        func lotsOfInput()
    }
    
    class GPUImageOutputWithInput: GPUImageOutput, GPUImageInput
    {
        func lotsOfInput() {
            println("lotsOfInput")
        }
    }
    
    // ...
    
    var someGpuImage = GPUImageOutput()
    var specificGpuImage = GPUImageOutputWithInput()
    
    for image in [someGpuImage, specificGpuImage] {
        if let specificImage = image as? GPUImageInput {
            specificImage.lotsOfInput()
        } else {
            println("the less specific type")
        }
    }
    

    更新:现在我明白你在哪里/为什么有这些类型......

    GPUImage 似乎有一个 swift 示例,可以做你想做的,尽可能 Swift-ly。

    here:

    class FilterOperation<FilterClass: GPUImageOutput where FilterClass: GPUImageInput>: FilterOperationInterface {
    ...
    

    type constraint syntax 也可以应用于函数,并且使用 where clause,这可能与您直接在 Swift 中获得的一样好。

    我越是试图了解如何移植这个有些常见的 objc 比喻,我就越意识到这是最 Swift 的方式。当我看到 GPUImage 本身 中的示例时,我确信这至少是您的答案。 :-)

    更新 2:所以,除了我上面链接到的使用 Swift 的特定 GPUImage 示例之外,我越来越多地想到这一点,要么使用 where 子句来保护 setter 函数,要么使用可计算属性来过滤set 功能似乎是唯一的出路。

    我想出了这个策略:

    import Foundation
    
    @objc protocol SpecialProtocol {
        func special()
    }
    
    class MyClass {}
    
    class MyClassPlus: MyClass, SpecialProtocol {
        func special() {
            println("I'm special")
        }
    }
    
    class MyContainer {
        private var i: MyClass?
    
        var test: MyClass? {
            get {
                return self.i
            }
            set (newValue) {
                if newValue is SpecialProtocol {
                   self.i = newValue
                }
            }
        }
    }
    
    var container = MyContainer()
    
    println("should be nil: \(container.test)")
    
    container.test = MyClass()
    println("should still be nil: \(container.test)")
    
    container.test = MyClassPlus()
    println("should be set: \(container.test)")
    
    (container.test as? MyClassPlus)?.special()
    

    输出:

    should be nil: nil
    should still be nil: nil
    should be set: Optional(main.MyClassPlus)
    I'm special
    

    (或者,您也可以使用precondition(newValue is SpecialProtocol, "newValue did not conform to SpecialProtocol") 代替is 检查,但这就像assert() 在不满足情况时会使应用程序崩溃。取决于您的需要。)

    @rintaro 的答案是一个很好的答案,并且是使用 where 子句作为保护的一个很好的例子(无论是功能上的还是 Swift 上的)。但是,当存在可计算属性时,我只是讨厌编写 setFoo() 函数。再说一次,即使使用可计算属性也有代码异味,因为我们似乎无法对set'er 应用泛型类型约束,并且必须在线进行协议一致性测试。

    【讨论】:

    • 抱歉,GPUImageOutputGPUImageInputpre-existent,所以我不能以任何旧方式重新声明它们。将更新我的问题以澄清!
    • 这只是一个例子,因为我不知道你的基类是什么!关键是@objc 协议,它允许您检测一致性。
    • ... 以及 +1 的问题 - 这是 Swift 与 ObjC 差异的一个非常有趣的案例。
    【解决方案4】:

    您可以使用typealias 关键字。操作方法如下:

    typealias MyNewType = MyExistingType
    

    MyExistingType 是协议、函数还是枚举都没有关系。它只需要某种类型。最好的部分是您可以对其应用访问控制。你可以说

    private typealias MyNewType = MyExistingType
    

    这使得 MyNewType 只能在定义的上下文中访问。

    【讨论】:

    • 这个问题是专门询问如何为实现协议的类的对象声明类型别名,而不仅仅是一个或另一个。
    猜你喜欢
    • 2020-07-29
    • 2018-08-21
    • 2014-08-12
    • 2014-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多