我在自己的項(xiàng)目中使用網(wǎng)絡(luò)請(qǐng)求是通過(guò)的自己寫(xiě)的 urlSession 的封裝庫(kù)(參考了不少 Alamofire的源碼)撞反。
前些天在將封裝庫(kù)重構(gòu)成面向協(xié)議的時(shí)候,想使用泛型函數(shù)來(lái)使 complete-handler 能夠處理不同的返回參數(shù)搪花。具體如何使用我在最后簡(jiǎn)單說(shuō)一下遏片。
我希望能夠定義一個(gè)協(xié)議 FormDataCreatable
垛膝,需要實(shí)現(xiàn)一個(gè)接收 NSData 然后轉(zhuǎn)化為自身類(lèi)型的靜態(tài)方法,類(lèi)似于這樣:
protocol FromDataCreatable {
static func fromData(data: NSData) throws -> Self?
}
比如說(shuō)我希望我得到一個(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 來(lái)采用這個(gè)協(xié)議的時(shí)候吼拥,就不能實(shí)現(xiàn)這個(gè)靜態(tài)方法了:
(為什么要 NSData 實(shí)現(xiàn)這個(gè)協(xié)議?一開(kāi)始說(shuō)了线衫,我希望能傳進(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
也是同樣的問(wèn)題。其中一個(gè)回答說(shuō):
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ǔ)言枯跑,自然應(yīng)該在編譯的時(shí)候做出更多限制。
然后今天在看 The Swift Programming Language (Swift 3) 的時(shí)候白热,注意到了一個(gè)以前沒(méi)有用過(guò)于是忘記了的 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
}
接下來(lái)讓 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
}
}
非常簡(jiǎn)單吧纳击?
還不只是這樣,讓別的類(lèi)型實(shí)現(xiàn)這個(gè)也是非常容易的攻臀,UIImage焕数、NSData 甚至都不需要實(shí)現(xiàn),直接 adopt 就可以了:
extension UIImage: FromDataCreatable { }
extension NSData: FromDataCreatable { }
解決了問(wèn)題之后刨啸,這是我在 StackOverFlow 上的回答:
希望能夠有所幫助吧堡赔。
接下來(lái)簡(jiǎn)單說(shuō)一下我定義這個(gè)是用來(lái)做什么吧。
一般 handler 都是handle response设联,這里為了簡(jiǎn)單表示就省去這一步:
我在NetConnectable
協(xié)議中寫(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)
}
這樣一來(lái)善已,在任何 adopt 了NetConnectable
協(xié)議的類(lèi)中,都能夠這么寫(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ù)离例,編譯器會(huì)幫你判斷類(lèi)型换团。