你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS網絡庫Alamofire內部實現初探

iOS網絡庫Alamofire內部實現初探

編輯:IOS開發綜合
一、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 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)
          這四個方法調用的最底層函數仍然是上一個泛型函數,區別是傳入的序列化器不同,由於函數語句
          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]) // http://httpbin.org/get?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:采用NSJSONSerialization來生成參數的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 }

           

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved