记一次 StackOverFlow 问题回答 —— Protocol Adopting Class Method returning Self

我在自己的项目中使用网络请求是通过的自己写的 urlSession 的封装库(参考了不少 Alamofire的源码)。
前些天在将封装库重构成面向协议的时候,想使用泛型函数来使 complete-handler 能够处理不同的返回参数。具体如何使用我在最后简单说一下。
我希望能够定义一个协议 FormDataCreatable,需要实现一个接收 NSData 然后转化为自身类型的静态方法,类似于这样:

protocol FromDataCreatable {
    static func fromData(data: NSData) throws -> Self?
}

比如说我希望我得到一个从 urlSession 中得到的 data 能转化成 Dictionary<String,AnyObject>
那我就这样实现(有点绕,使用的是 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 {}

但是在我转用 NSData 来采用这个协议的时候,就不能实现这个静态方法了:
(为什么要 NSData 实现这个协议?一开始说了,我希望能传进一个泛型 complete-handler)

extension NSData: FromDataCreatable {
    public static func formData(data: NSData) throws -> NSData {
    //报错:method 'formData' in non-final class 'NSData' must return `Self` to conform to protocol 'FromDataCreatable'
        return data
    }
}

我在 StackOverFlow 上找寻答案的时候,找到了这个:
Method in non-final class must return Self to conform to protocol
也是同样的问题。其中一个回答说:

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 作为一个静态语言,自然应该在编译的时候做出更多限制。

然后今天在看 The Swift Programming Language (Swift 3) 的时候,注意到了一个以前没有用过于是忘记了的 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)
}

返回一个 Self ,不就相当于初始化?
于是我将 protocol 改成了这样。注意这是一个可以失败的初始化器:

protocol FromDataCreatable {
    init?(data: NSData) throws
}

接下来让 Dictionary 实现这个协议:

extension Dictionary: FromDataCreatable {
    init?(data: NSData) throws {
        if let json = try NSJSONSerialization.JSONObjectWithData(
            data, options: []) as? Dictionary {
            self = json
        }
        return nil
    }
}

非常简单吧?
还不只是这样,让别的类型实现这个也是非常容易的,UIImage、NSData 甚至都不需要实现,直接 adopt 就可以了:

extension UIImage: FromDataCreatable { }

extension NSData: FromDataCreatable { }

解决了问题之后,这是我在 StackOverFlow 上的回答:

StackOverFlow

希望能够有所帮助吧。

接下来简单说一下我定义这个是用来做什么吧。
一般 handler 都是handle response,这里为了简单表示就省去这一步:
我在NetConnectable协议中写了一个默认实现的方法:

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协议的类中,都能够这么写:

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做一些什么
}

而且都只是用的这一个泛型函数,编译器会帮你判断类型。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容