如何在Swift中创建通用协议?


85

我想使用一种接受通用输入并返回通用值的方法来创建协议。

到目前为止,这是我尝试过的方法,但是会产生语法错误。

使用未声明的标识符T。

我究竟做错了什么?

protocol ApiMapperProtocol {
    func MapFromSource(T) -> U
}

class UserMapper: NSObject, ApiMapperProtocol {
    func MapFromSource(data: NSDictionary) -> UserModel {
        var user = UserModel() as UserModel
        var accountsData:NSArray = data["Accounts"] as NSArray     
        return user
    } 
}

Answers:


141

协议略有不同。查看Apple文档中的“关联类型” 。

这就是您在示例中使用它的方式

protocol ApiMapperProtocol {
    associatedtype T
    associatedtype U
    func MapFromSource(_:T) -> U
}

class UserMapper: NSObject, ApiMapperProtocol {
    typealias T = NSDictionary
    typealias U = UserModel

    func MapFromSource(_ data:NSDictionary) -> UserModel {
        var user = UserModel()
        var accountsData:NSArray = data["Accounts"] as NSArray
        // For Swift 1.2, you need this line instead
        // var accountsData:NSArray = data["Accounts"] as! NSArray
        return user
    }
}

5
注意,ApiMapperProtocol的唯一目的是用于一般约束。这不是一样可以写出让X:ApiMapperProtocol = UserMapper()

18
苹果为何坚持要使一切变得如此直观?
deusprogrammer

@Ben在这种情况下如何实现让x:ApiMapperProtocol = UserMapper()?
denis_lor

@denis_lor如果x是本地的,则无需显式说明其类型,因此let x = UserMapper()
Ben Leggiero

2
@BenLeggiero我刚刚发现,如果在中间通用类中使用a,您可以做让x:ApiMapperProtocol = UserMapper()之类的
事情

21

要在阐述娄佛朗哥的回答了一下,如果你想创建一个使用一个特定的方法ApiMapperProtocol,这样做正是如此:

protocol ApiMapperProtocol {
    associatedtype T
    associatedtype U
    func mapFromSource(T) -> U
}

class UserMapper: NSObject, ApiMapperProtocol {
    // these typealiases aren't required, but I'm including them for clarity
    // Normally, you just allow swift to infer them
    typealias T = NSDictionary 
    typealias U = UserModel

    func mapFromSource(data: NSDictionary) -> UserModel {
        var user = UserModel()
        var accountsData: NSArray = data["Accounts"] as NSArray
        // For Swift 1.2, you need this line instead
        // var accountsData: NSArray = data["Accounts"] as! NSArray
        return user
    }
}

class UsesApiMapperProtocol {
    func usesApiMapperProtocol<
        SourceType,
        MappedType,
        ApiMapperProtocolType: ApiMapperProtocol where
          ApiMapperProtocolType.T == SourceType,
          ApiMapperProtocolType.U == MappedType>(
          apiMapperProtocol: ApiMapperProtocolType, 
          source: SourceType) -> MappedType {
        return apiMapperProtocol.mapFromSource(source)
    }
}

UsesApiMapperProtocol现在保证只接受SourceType与给定兼容的ApiMapperProtocol

let dictionary: NSDictionary = ...
let uses = UsesApiMapperProtocol()
let userModel: UserModel = uses.usesApiMapperProtocol(UserMapper()
    source: dictionary)

这是一篇非常好的文章,值得推荐。几个愚蠢的问题:为什么他们决定使用Swift 1.2as!而不是as?第二:您能告诉我为什么我们需要在符合协议的类中type alias再次定义(即typealias T = NSDictionary typealias U = UserModel)吗?提前致谢。
Unheilig 2015年

我不知道为什么他们从切换asas!。检查devforum。
Heath Borders

typealias T=NSDictionary并且typealias U=UserModel不是必需的。我更新了示例以反映这一点。
Heath Borders'Apr

2
如!表明它可能会失败。使开发人员更清楚。
user965972

这是答案的底部。
Heath Borders

4

为了获得泛型并像这样声明,let userMapper: ApiMapperProtocol = UserMapper()您必须具有符合返回泛型元素的协议的泛型类。

protocol ApiMapperProtocol {
    associatedtype I
    associatedType O
    func MapFromSource(data: I) -> O
}

class ApiMapper<I, O>: ApiMapperProtocol {
    func MapFromSource(data: I) -> O {
        fatalError() // Should be always overridden by the class
    }
}

class UserMapper: NSObject, ApiMapper<NSDictionary, UserModel> {
    override func MapFromSource(data: NSDictionary) -> UserModel {
        var user = UserModel() as UserModel
        var accountsData:NSArray = data["Accounts"] as NSArray     
        return user
    } 
}

现在,您也可以将userMapper称为,ApiMapper它具有针对以下方面的特定实现UserMapper

let userMapper: ApiMapper = UserMapper()
let userModel: UserModel = userMapper.MapFromSource(data: ...)

在这种情况下拥有协议有什么意义?的声明中未使用它userMapper
alekop

-1

如何创建和使用通用协议

通用协议{

associatedtype T
associatedtype U

func operation(_ t:T)->U

}

//使用通用协议

struct Test:Generic {

typealias T = UserModel
typealias U = Any

func operation(_ t: UserModel)->Any {
    let dict = ["name":"saurabh"]
    return dict
    
} 

}


-3

您可以使用带有类型擦除的模板方法...

protocol HeavyDelegate : class {
  func heavy<P, R>(heavy: Heavy<P, R>, shouldReturn: P) -> R
}  

class Heavy<P, R> {
    typealias Param = P
    typealias Return = R
    weak var delegate : HeavyDelegate?  
    func inject(p : P) -> R? {  
        if delegate != nil {
            return delegate?.heavy(self, shouldReturn: p)
        }  
        return nil  
    }
    func callMe(r : Return) {
    }
}
class Delegate : HeavyDelegate {
    typealias H = Heavy<(Int, String), String>

    func heavy<P, R>(heavy: Heavy<P, R>, shouldReturn: P) -> R {
        let h = heavy as! H
        h.callMe("Hello")
        print("Invoked")
        return "Hello" as! R
    }  
}

let heavy = Heavy<(Int, String), String>()
let delegate = Delegate()
heavy.delegate = delegate
heavy.inject((5, "alive"))

2
这篇文章没有任何解释。您也已按原样将其发布在stackoverflow.com/questions/28614990/…上
user1427799 '16
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.