我在自己的項(xiàng)目中使用網(wǎng)絡(luò)請求是通過的自己寫的 urlSession 的封裝庫(參考了不少 Alamofire的源碼)。
前些天在將封裝庫重構(gòu)成面向協(xié)議的時(shí)候,想使用泛型函數(shù)來使 complete-handler 能夠處理不同的返回參數(shù)。具體如何使用我在最后簡單說一下。
我希望能夠定義一個(gè)協(xié)議 FormDataCreatable,需要實(shí)現(xiàn)一個(gè)接收 NSData 然后轉(zhuǎn)化為自身類型的靜態(tài)方法,類似于這樣:
protocol FromDataCreatable {
static func fromData(data: NSData) throws -> Self?
}
比如說我希望我得到一個(gè)從 urlSession 中得到的 data 能轉(zhuǎn)化成 Dictionary<String,AnyObject>
那我就這樣實(shí)現(xiàn)(有點(diǎn)繞,使用的是 extend-protocol )
extension FromDataCreatable where Self: Dictionary<String,AnyObject> {
static func formData(data: NSData) throws -> Self {
if let json = try NSJSONSerialization.JSONObjectWithData(
data, options: []) as? Dictionary<String,AnyObject> {
return json
}
return [ : ]
}
}
extension Dictionary: FormDataCreatable {}
但是在我轉(zhuǎn)用 NSData 來采用這個(gè)協(xié)議的時(shí)候,就不能實(shí)現(xiàn)這個(gè)靜態(tài)方法了:
(為什么要 NSData 實(shí)現(xiàn)這個(gè)協(xié)議?一開始說了,我希望能傳進(jìn)一個(gè)泛型 complete-handler)
extension NSData: FromDataCreatable {
public static func formData(data: NSData) throws -> NSData {
//報(bào)錯(cuò):method 'formData' in non-final class 'NSData' must return `Self` to conform to protocol 'FromDataCreatable'
return data
}
}
我在 StackOverFlow 上找尋答案的時(shí)候,找到了這個(gè):
Method in non-final class must return Self to conform to protocol
也是同樣的問題。其中一個(gè)回答說:
The compiler doesn't know what Self is when it compiles f in the protocol extension and I think it assumes it must be the exact type of the class it is applying it too. With NSData, that might not be the case because you might have a subclass of it.
很有道理,Swift 作為一個(gè)靜態(tài)語言,自然應(yīng)該在編譯的時(shí)候做出更多限制。
然后今天在看 The Swift Programming Language (Swift 3) 的時(shí)候,注意到了一個(gè)以前沒有用過于是忘記了的 protocol 特性:
Initializer Requirements
Protocols can require specific initializers to be implemented by conforming types. You write these initializers as part of the protocol’s definition in exactly the same way as for normal initializers, but without curly braces or an initializer body:
protocol SomeProtocol {
init(someParameter: Int)
}
返回一個(gè) Self ,不就相當(dāng)于初始化?
于是我將 protocol 改成了這樣。注意這是一個(gè)可以失敗的初始化器:
protocol FromDataCreatable {
init?(data: NSData) throws
}
接下來讓 Dictionary 實(shí)現(xiàn)這個(gè)協(xié)議:
extension Dictionary: FromDataCreatable {
init?(data: NSData) throws {
if let json = try NSJSONSerialization.JSONObjectWithData(
data, options: []) as? Dictionary {
self = json
}
return nil
}
}
非常簡單吧?
還不只是這樣,讓別的類型實(shí)現(xiàn)這個(gè)也是非常容易的,UIImage、NSData 甚至都不需要實(shí)現(xiàn),直接 adopt 就可以了:
extension UIImage: FromDataCreatable { }
extension NSData: FromDataCreatable { }
解決了問題之后,這是我在 StackOverFlow 上的回答:

希望能夠有所幫助吧。
接下來簡單說一下我定義這個(gè)是用來做什么吧。
一般 handler 都是handle response,這里為了簡單表示就省去這一步:
我在NetConnectable協(xié)議中寫了一個(gè)默認(rèn)實(shí)現(xiàn)的方法:
public func request<T: FromDataCreatable>(
action: Action,
params: [String : AnyObject]?,
handler: T -> Void ) {
let manager = Manager(
url: action.rawValue,
params: params)
manager.startRequest(handler)
}
這樣一來,在任何 adopt 了NetConnectable協(xié)議的類中,都能夠這么寫:
self.request(Action.getPhoto, params: [userId : 2]) {
doSomeThingWithImage($0)
}
func doSomeThingWithImage(image: UIImage) {
//使用圖片做一些什么
}
又或者:
self.request(Action.getUser, params: [userId : 2]) {
doSomeThingWithJSON($0)
}
func doSomeThingWithJSON(json: [String : AnyObject]) {
//使用json做一些什么
}
而且都只是用的這一個(gè)泛型函數(shù),編譯器會幫你判斷類型。