What's the Swift equivalent of declaring `typedef SomeClass<SomeProtocol> MyType`?

旧时模样 提交于 2019-12-29 07:01:14

问题


I’m currently writing some Swift code in a project that is predominately Objective-C. In our ObjC code, we have a header that declares typedef GPUImageOutput<GPUImageInput> MyFilter;. We can then declare e.g. a @property that can only be a GPUImageOutput subclass that implements GPUImageInput.

(NOTE: GPUImageOutput and GPUImageInput are not defined by me; they are part of the GPUImage library)

Our Swift code doesn't seem to recognize this, even though the header is #imported in our Bridging Header. I’ve tried to replicate the declaration in Swift, but neither of these are proper syntax:
typealias MyFilter = GPUImageOutput, GPUImageInput
typealias MyFilter = GPUImageOutput : GPUImageInput


回答1:


In Swift 4 you can achieve this with the new & sign (Below an example of a parameter confirming to UIViewController and UITableViewDataSource:

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



回答2:


You can't declare typealias like that.

The best we can do is something like this:

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



回答3:


In Swift, something like the following should accomplish your task, but it's different than its ObjC counterpart:

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")
    }
}

UPDATE: now that I understand where/why you have these types ...

GPUImage seems to have a swift example that does what you want, as Swift-ly as possible.

See here:

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

The type constraint syntax can be applied to functions, too, and with a where clause, that's probably as good as you're going to get directly in Swift.

The more I tried to understand how to port this somewhat common objc trope, the more I realized it was the most Swift-way. Once I saw the example in GPUImage itself, I was convinced it was at least your answer. :-)

UPDATE 2: So, besides the specific GPUImage example I linked to above that uses Swift, the more and more I think about this, either using a where clause to guard the setter function, or using a computable property to filter the set functionality seems the only way to go.

I came up with this strategy:

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

Outputs:

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

(Optionally, you could also use precondition(newValue is SpecialProtocol, "newValue did not conform to SpecialProtocol") in place of the is check, but that will act like an assert() can crash the app if the case isn't met. Depends on your needs.)

@rintaro's answer is a good one, and is a good example of using a where clause as a guard (both nice functional-ly, and Swift-ly). However, I just hate to write a setFoo() function when computable properties exist. Then again, even using a computable property has code smell, since we can't seem to be able to apply a generic type-constraint to the set'er, and have to do the protocol conformance test in-line.




回答4:


You can use typealias keyword. Here is how to do it:

typealias MyNewType = MyExistingType

It doesn't matter whether MyExistingType is protocol or function or enum. All it needs to be some type. And the best part is you can apply access control on it. You can say

private typealias MyNewType = MyExistingType

That makes MyNewType is only accessible the context that is defined in.



来源:https://stackoverflow.com/questions/26474061/whats-the-swift-equivalent-of-declaring-typedef-someclasssomeprotocol-mytype

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!