美文网首页
记一次 StackOverFlow 问题回答 —— Protoc

记一次 StackOverFlow 问题回答 —— Protoc

作者: Frain | 来源:发表于2016-06-26 02:41 被阅读0次

    我在自己的项目中使用网络请求是通过的自己写的 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做一些什么
    }
    

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

    相关文章

      网友评论

          本文标题:记一次 StackOverFlow 问题回答 —— Protoc

          本文链接:https://www.haomeiwen.com/subject/kfpadttx.html