iOS网络库Alamofire内部实现初探

一、Making a Request

Alamofire. request (. GET , URLString: "http://httpbin.org/get" )

该方法调用了Alamofire.swift中的

public func request(method: Method , URLString: URLStringConvertible , parameters: [ String : AnyObject ]? = nil , encoding: ParameterEncoding = . URL , headers: [ String : String ]? = nil ) -> Request {
   
return Manager .sharedInstance. request (method, URLString, parameters: parameters, encoding: encoding, headers: headers)
}

接收的第一个参数为HTTP请求方法之一,第二个参数为请求的domain,第三个参数用来传入 查询参数,与第四个参数相配合,若第四个参数此时默认的ParameterEncoding.URL未被改变,则将传入的查询参数按照URL方式进行编码,并添加到domain后,具体为将所给键值对以'?'为起点,通过&分隔开,再以=分割Key-value,传入的第五个参数为HTTP请求报文中的请求头

通过查看源码发现返回值中的Manager类是对NSURLSession网络接口的封装,sharedIntance方法返回一个采用NSURLSessionConfiguration.defaultSessionConfiguration并对其传入硬编码默认HTTP请求头的Manage类实例,Manage类的具体实现不在这里讲,之后调用的request是Manage的类方法

    public func request(
        method:
Method ,
       
_ URLString: URLStringConvertible ,
        parameters: [
String : AnyObject ]? = nil ,
        encoding:
ParameterEncoding = . URL ,
        headers: [
String : String ]? = nil )
        ->
Request
    {
       
let mutableURLRequest = URLRequest (method, URLString: URLString, headers: headers)
       
let encodedURLRequest = encoding. encode (mutableURLRequest, parameters: parameters). 0
       
return request (encodedURLRequest)
    }

该方法获取传入Alamofire.request()方法的相同参数,根据传入的编码方式具体的对URL进行编码,并将编码后的encodedURLRequest作为初始化参数传入Request类生成实例并返回,Request类将在下面讲解

switch self {
       
case .URL:
           
func query(parameters: [ String : AnyObject ]) -> String {
               
var components: [( String , String )] = []
               
for key in Array (parameters. keys ). sort (<) {
                   
let value: AnyObject ! = parameters[key]
                    components +=
queryComponents (key, value)
                }

               
return "&" . join (components. map { " \ ( $0 )= \ ( $1 )" } as [ String ])
            }

上方代码为编码函数节选,当编码方式选为URL时,具体实现了前面提到过的 将所给键值对以'?'为起点,通过&分隔开,再以=分割Key-value

二、Response Handling

        Alamofire. request (. GET , URLString: "http://httpbin.org/get" , parameters: [ "foo" : "bar" ])
            .
response { request, response, data, error in
               
print (request)
               
print (response)
               
print (error)
        }

Alamofire的网络操作是异步的,不会阻塞主线程,这里调用的response方法是Request的类扩展方法,Request类是对NSURLSessionTask的封装,参数位置传入的闭包当请求被执行完成时就会被调用

    public func response(completionHandler: ( NSURLRequest , NSHTTPURLResponse ?, NSData ?, NSError ?) -> Void ) -> Self {
       
return response (responseSerializer: Request . dataResponseSerializer (), completionHandler: completionHandler)
    }

该函数接收一个闭包做参数,该闭包又与Request的通用序列化方法返回的序列化器一起被作为参数传入更底层的函数中

    public func response<T: ResponseSerializer, V where T.SerializedObject == V>(
        queue:
dispatch_queue_t ? = nil ,
        responseSerializer:
T ,
        completionHandler: (
NSURLRequest , NSHTTPURLResponse ?, V ?, NSError ?) -> Void )
        ->
Self
    {
       
delegate . queue . addOperationWithBlock {
           
let result: V ?
           
let error: NSError ?

            (result, error) = responseSerializer.
serializeResponse ( self . request , self . response , self . delegate . data )

           
dispatch_async (queue ?? dispatch_get_main_queue ()) {
                completionHandler(
self . request , self . response , result, self . delegate . error ?? error)
            }
        }

       
return self
    }
}

该函数仍是Request类的拓展方法,该函数采用泛型,传入的第一个参数序列化器T需满足ResponseSerializer协议,而第二个参数闭包的第三个参数V的类型需与T的预计序列化对象相同

addOperationWithBlock是NSOperationQueue的类型方法,用于在该队列中添加用于执行的闭包操作,在该闭包中完成将取得的各类数据序列化后送给传入的闭包作为参数

三、Response Serialization

Alamofire内置的响应方法有四个:
  • response()
  • responseString(encoding: NSStringEncoding)
  • responseJSON(options: NSJSONReadingOptions)
  • responsePropertyList(options: NSPropertyListReadOptions)

这四个方法调用的最底层函数仍然是上一个泛型函数,区别是传入的序列化器不同,由于函数语句

<T: ResponseSerializer, V where T.SerializedObject == V>

的存在,传入不同的序列化器所处理并返回的数据类型也不同

四、HTTP Methods

Alamofire当前支持的HTTP方法共有九种

    public enum Method: String {
       
case OPTIONS = "OPTIONS"
       
case GET = "GET"
       
case HEAD = "HEAD"
       
case POST = "POST"
       
case PUT = "PUT"
       
case PATCH = "PATCH"
       
case DELETE = "DELETE"
       
case TRACE = "TRACE"
       
case CONNECT = "CONNECT"
    }

通常作为Alamofire.request的第一个参数传入

五、Parameters

        Alamofire. request (. GET , URLString: "http://httpbin.org/get" , parameters: [ "foo" : "bar" ])

        let parameters = [
           
"foo" : "bar" ,
           
"baz" : [ "a" , 1 ],
           
"qux" : [
               
"x" : 1 ,
               
"y" : 2 ,
               
"z" : 3
            ]
        ]

        Alamofire. request (. POST , URLString: "http://httpbin.org/post" , parameters: parameters)
        // HTTP body: foo=bar&baz[]=a&baz[]=1&qux[x]=1&qux[y]=2&qux[z]=3

传入的参数会调用第一部分 Making a Request 中的函数,按照HTTP报文所需的格式添加在URL后面

六、Parameter Encoding

        enum ParameterEncoding {
           
case URL
           
case JSON
           
case PropertyList(format: NSPropertyListFormat, options: NSPropertyListWriteOptions)
           
case Custom((URLRequestConvertible, [String: AnyObject]?) -> (NSMutableURLRequest, NSError?))
           
           
func encode(request: NSURLRequest , parameters: [ String : AnyObject ]?) -> ( NSURLRequest , NSError ?)
            {...}
        }

在Alamofire中提供的对参数的编码方式有很多种,前几部分的编码方式默认为URL

URL:在 GET HEAD DELETE 方法的URL后面添加查询参数,或者为其它HTTP方法配置URL,以这种方式被编码的HTTP请求报文的请求头会被设置为 application/x-www-form-urlencoded ,对于传入的参数,若是数组,key为空,若为字典或键值对则会正常配对key=value
JSON:采用NSJSON Serialization来生成参数的JSON表示形式,此时请求头会被设置为 application/json
PropertyList:采用NSPropertyListSerialization并依据请求中的相关格式来生成参数的plist表示形式,请求头为 application/x-plist
Custom:依据相关闭包生成请求并设置合适的参数

        let parameters = [
           
"foo" : [ 1 , 2 , 3 ],
           
"bar" : [
               
"baz" : "qux"
            ]
        ]
       
        Alamofire.request(.POST,
"http://httpbin.org/post" , parameters: parameters, encoding: .JSON)
        // HTTP body: {"foo": [1, 2, 3], "bar": {"baz": "qux"}}

上方代码为调用.JSON编码方式对POST方法所需的相关参数进行编码的操作过程及结果

七、HTTP Header

在全局方法Request中可以通过传入参数很方便的修改HTTP报文中的请求头,对于某些不能通过改Request改请求头的,推荐的方法是通过NSURLSesstionConfiguration直接改NSURLSession,这样其生成的NSURLSessionTask,也就是Request类底层的请求头自然会被改变

八、Caching

获取或设置可共享的缓存
sharedURLCache()
setSharedURLCache(_:)

创建新的缓存对象
init(memoryCapacity:diskCapacity:diskPath:)

获取和存储缓存对象
cachedResponseForRequest(_:)
storeCachedResponse(_:forRequest)

移除缓存对象
removeAllCachedResponses()
removeCachedResponseForRequest(_:)

获取和设置硬盘缓存属性
currentDiskUsage
diskCapacity

获取和设置内存缓存属性
currentMemoryUsage
memoryCapacity

九、Uploading

支持上传的类型有File、Data、Stream、MultipartFormData

        let fileURL = NSBundle . mainBundle (). URLForResource ( "Default" , withExtension: "png" )
        Alamofire.upload(.POST, URLString: "http://httpbin.org/post" , file: fileURL!)

参数分别为POST、目标URL、本地源URL

public func upload(method: Method , URLString: URLStringConvertible , headers: [ String : String ]? = nil , file: NSURL ) -> Request {
   
return Manager .sharedInstance. upload (method, URLString, headers: headers, file: file)
}

同样的,调用Manager.sharedInstance生成Manager实例,调用其upload类方法

    public func upload(URLRequest: URLRequestConvertible , file: NSURL ) -> Request {
       
return upload (. File (URLRequest. URLRequest , file))
    }

该Public方法实际上作为了类内同名Private方法的外部接口,由于调用时选取的函数为File类型,所以该Public函数接口将接收到的参数打包传入File类型中

        case .File( let request, let fileURL):
           
dispatch_sync ( queue ) {
                uploadTask =
self . session . uploadTaskWithRequest (request, fromFile: fileURL)
            }

上方为Private函数节选,在内部进行异步上传后,该函数将最终发出的Request作为参数返回

        Alamofire. upload (. POST , URLString: "http://httpbin.org/post" , file: fileURL!)
            .
progress { bytesWritten, totalBytesWritten, totalBytesExpectedToWrite in
               
print (totalBytesWritten)
            }
            .
responseJSON { request, response, JSON, error in
               
print (JSON)
        }

.Progress为Request的类方法,判断当前Request的delegate属性的类型并将传入的闭包赋予delegate特定的progress属性,最终返回当前Request

将闭包赋予对应progress后,当Request的内部定义类的

        func URLSession(session: NSURLSession , task: NSURLSessionTask , didSendBodyData bytesSent: Int64 , totalBytesSent: Int64 , totalBytesExpectedToSend: Int64 ) {
           
if let taskDidSendBodyData = taskDidSendBodyData {
                taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend)
            }
else {
               
progress . totalUnitCount = totalBytesExpectedToSend
               
progress . completedUnitCount = totalBytesSent

               
uploadProgress ?(bytesSent, totalBytesSent, totalBytesExpectedToSend)
            }
        }

代理方法被调用

十、Downloading

支持下载的类型有Request、Resume Data

        Alamofire. download (. GET , URLString: "http://httpbin.org/stream/100" , destination: { (temporaryURL, response) in
           
if let directoryURL = NSFileManager . defaultManager ()
                .
URLsForDirectory (. DocumentDirectory ,
                    inDomains: .UserDomainMask)[
0 ]
               
as ? NSURL {
                   
let pathComponent = response. suggestedFilename
                   
                   
return directoryURL. URLByAppendingPathComponent (pathComponent!)
            }
           
           
return temporaryURL
        })

第一个参数为GET方法,第二个为URL,第三个传入的参数是个闭包,从中生成下载源地址

内部封装的函数仍然调用的是Manager.sharedInstance的实例方法download,该方法又经过若干次封装,具体功能实现函数是位于Manager拓展内的private方法

    private func download(downloadable: Downloadable , destination: Request . DownloadFileDestination ) -> Request {
       
var downloadTask: NSURLSessionDownloadTask !

       
switch downloadable {
       
case .Request( let request):
           
dispatch_sync ( queue ) {
                downloadTask =
self . session . downloadTaskWithRequest (request)
            }
       
case .ResumeData( let resumeData):
           
dispatch_sync ( queue ) {
                downloadTask =
self . session . downloadTaskWithResumeData (resumeData)
            }
        }

       
let request = Request (session: session , task: downloadTask)

       
if let downloadDelegate = request. delegate as ? Request . DownloadTaskDelegate {
            downloadDelegate.
downloadTaskDidFinishDownloadingToURL = { session, downloadTask, URL in
               
return destination(URL, downloadTask. response as ! NSHTTPURLResponse )
            }
        }

       
delegate [request. delegate . task ] = request. delegate

       
if startRequestsImmediately {
            request.
resume ()
        }

       
return request
    }

函数内部以异步方式执行NSURLSession的downloadTaskWith方法,最终返回发出的request

        let destination = Alamofire. Request . suggestedDownloadDestination (. DocumentDirectory , domain: .UserDomainMask)
       
        Alamofire.download(.GET, URLString: "http://httpbin.org/stream/100", destination: destination)

该方法将使用默认的下载地址

十一、Validation

        Alamofire. request (. GET , URLString: "http://httpbin.org/get" , parameters: [ "foo" : "bar" ])
            .
validate (statusCode: 200 ..< 300 )
            .
validate (contentType: [ "application/json" ])
            .
response { ( _ , _ , _ , error) in
               
print (error)
        }

通过上述代码可以人工对返回HTTP报文的状态码和MIME类型进行检测,validate函数最终是对该Request拓展内的同名函数的封装

    public func validate(validation: Validation ) -> Self {
       
delegate . queue . addOperationWithBlock {
           
if let response = self . response where self . delegate . error == nil && !validation( self . request , response) {
               
self . delegate . error = NSError (domain: AlamofireErrorDomain , code: - 1 , userInfo: nil )
            }
        }

       
return self
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值