二嘁傀、詳解Alamofire(4.0.0)

Alamofire(4.0.0是基于Swift3.0語法)是Swift中最常用的網(wǎng)絡(luò)框架载荔。

1. AFError

Swift中的枚舉比較特殊和敬,可以在枚舉中定義方法凹炸,但是不能定義變量。

enum CompassPoint{
    case North
    case Sourth
    case East
    case West
    //枚舉中 可以定義方法
    func show(){
        print(self)
    }
}
// 定義枚舉變量
var p = CompassPoint.North
// 類型標(biāo)注之后 可以使用點來獲取枚舉值
var p2 : CompassPoint = .Sourth
p.show()
p2.show()

在Alamofire中的枚舉更加有個性昼弟,可以在枚舉中定義枚舉啤它,并且枚舉的case可以傳遞參數(shù)。異常類型可以直接通過throw拋出舱痘。

public enum AFError: Error {
  
    public enum ParameterEncodingFailureReason {
        case missingURL
        case jsonEncodingFailed(error: Error)
        case propertyListEncodingFailed(error: Error)
    }

    public enum MultipartEncodingFailureReason {
        ...
    }

    public enum ResponseValidationFailureReason {
        ...
    }

    public enum ResponseSerializationFailureReason {
        ...
    }

    case invalidURL(url: URLConvertible)
    case parameterEncodingFailed(reason: ParameterEncodingFailureReason)
    case multipartEncodingFailed(reason: MultipartEncodingFailureReason)
    case responseValidationFailed(reason: ResponseValidationFailureReason)
    case responseSerializationFailed(reason: ResponseSerializationFailureReason)
}

Swift中的枚舉非常強(qiáng)大变骡,可以對枚舉進(jìn)行擴(kuò)展,在擴(kuò)展中可以定義屬性芭逝。如下代碼所示塌碌,Alamofire中可以在AFError的擴(kuò)展中定義方法來判斷當(dāng)前的錯誤屬于哪一類錯誤。

    extension AFError {
    public var isInvalidURLError: Bool {
        if case .invalidURL = self { return true }
        return false
    }

     ......

    public var isResponseSerializationError: Bool {
        if case .responseSerializationFailed = self { return true }
        return false
    }
}

由于擴(kuò)展可以定義多個旬盯,所以便可以將一類功能歸類到同一個擴(kuò)展中台妆,如Alamofire中定義了一個便捷屬性擴(kuò)展翎猛。

// MARK: - Convenience Properties

extension AFError {
    public var urlConvertible: URLConvertible? {
        switch self {
        case .invalidURL(let url):
            return url
        default:
            return nil
        }
    }

    ......

    public var failedStringEncoding: String.Encoding? {
        switch self {
        case .responseSerializationFailed(let reason):
            return reason.failedStringEncoding
        default:
            return nil
        }
    }
}

Alamofire中枚舉使用的最牛逼的就是為AFError內(nèi)部枚舉添加相應(yīng)擴(kuò)展。

extension AFError.ParameterEncodingFailureReason {
    var underlyingError: Error? {
        switch self {
        case .jsonEncodingFailed(let error), .propertyListEncodingFailed(let error):
            return error
        default:
            return nil
        }
    }
} 

......

然后最上層直接調(diào)用underlyingError得到error

  • Summary

1.枚舉添加訪問修飾符接剩,并且可以實現(xiàn)協(xié)議办成。比如。public enum AFError: Error搂漠。這里的Error其實是一個協(xié)議public protocol Error迂卢。
2.枚舉內(nèi)部可以再定義枚舉。相當(dāng)于聲明枚舉桐汤,后面還是通過case的方式使用而克。并且可以傳遞參數(shù)。
3.通過擴(kuò)展給枚舉挺添加便捷屬性怔毛。
4.按照不同功能給擴(kuò)展分組员萍。讓代碼更便于閱讀。

2. Notifications

定義的通知是一件比較簡單的事情拣度。一般情況下碎绎,在OC中我們會直接定義一個字符串來表示某種通知。通常情況下也沒怎么把通知管理起來抗果。

在Alamofire中定義的通知就感覺很正式了筋帖。首先是成了一個擴(kuò)展的形式,把相關(guān)的通知都寫在里面冤馏。然后使用結(jié)構(gòu)體來包裝通知日麸。代碼如下:

extension Notification.Name {
    public struct Task {
        public static let DidResume = Notification.Name(rawValue: "org.alamofire.notification.name.task.didResume")

        public static let DidSuspend = Notification.Name(rawValue: "org.alamofire.notification.name.task.didSuspend")

        public static let DidCancel = Notification.Name(rawValue: "org.alamofire.notification.name.task.didCancel")

        public static let DidComplete = Notification.Name(rawValue: "org.alamofire.notification.name.task.didComplete")
    }
}
  • 注意定義的都是靜態(tài)常量

相當(dāng)于擴(kuò)展了系統(tǒng)通知name。把通知名稱都定義在里面逮光。然后通過不同的結(jié)構(gòu)體定義不同用途的通知代箭。其實在OC中也可以這樣做。只是平時很少這樣寫涕刚,這樣寫之后代碼組織就更加優(yōu)雅了嗡综。
Swift中Notification的API如下:

public struct Notification : ReferenceConvertible, Equatable, Hashable {

    public typealias ReferenceType = NSNotification

    public var name: Notification.Name

    public var object: Any?

    public var userInfo: [AnyHashable : Any]?

    public init(name: Notification.Name, object: Any? = default, userInfo: [AnyHashable : Any]? = default)

    public var hashValue: Int { get }

    public var description: String { get }

    public var debugDescription: String { get }

    public typealias Name = NSNotification.Name

    public static func ==(lhs: Notification, rhs: Notification) -> Bool
}
  • Summary :

1.通過擴(kuò)展Notification.Name來定義通知名稱。讓代碼組織更加優(yōu)雅杜漠。
2.使用結(jié)構(gòu)體來區(qū)分不同功能的通知极景。在結(jié)構(gòu)體下定義靜態(tài)常量定義通知名稱。

3. ParameterEncoding

詳細(xì)代碼見ParameterEncoding.swift類碑幅。

(1)枚舉繼承的類就是case所對應(yīng)的類型戴陡。比如

public enum HTTPMethod: 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"
}

(2)typealias可以給一個類型取一個別名

public typealias Parameters = [String: Any]

Parameters就是一個字典,key為字符串沟涨,value可以是任意類型

4. Request

Request.swift這個類中有三個協(xié)議(RequestAdapter、RequestRetrier异吻、TaskConvertible)裹赴、五個類喜庞、六個擴(kuò)展。

四個類及之間的繼承關(guān)系如下:

open class Request
open class DataRequest: Request
open class DownloadRequest: Request
open class UploadRequest: DataRequest
open class StreamRequest: Request

一開始先定義一套協(xié)議棋返,和類型別名(類型別名)延都。如下:

public protocol RequestAdapter {
    func adapt(_ urlRequest: URLRequest) throws -> URLRequest
}

// 類似于block
public typealias RequestRetryCompletion = (_ shouldRetry: Bool, _ timeDelay: TimeInterval) -> Void

public protocol RequestRetrier {
    func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion)
}

protocol TaskConvertible {
    func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask
} 

defer關(guān)鍵字:表示在執(zhí)行完方法最后的時候調(diào)用。比如文件打開后最后需要關(guān)閉睛竣。
internal(set):表示set方法只有在內(nèi)部模塊才能訪問晰房。get方法是都能訪問的

open internal(set) var delegate: TaskDelegate {
    get {
        taskDelegateLock.lock() ; defer { taskDelegateLock.unlock() }
        return taskDelegate
    }
    set {
        taskDelegateLock.lock() ; defer { taskDelegateLock.unlock() }
        taskDelegate = newValue
    }
}

Swift 3.0 中方法的返回值必須有接收否則會報警告,當(dāng)然其實主要目的是為了避免開發(fā)人員忘記接收返回值的情況射沟,但是有些情況下確實不需要使用返回值可以使用"_"接收來忽略返回值殊者。當(dāng)然你也可以增加@discardableResult聲明,告訴編譯器此方法可以不用接收返回值验夯。如Request.Swift類的第187行:

@discardableResult
open func authenticate(usingCredential credential: URLCredential) -> Self {
     delegate.credential = credential
     return self
}

@noescape: 用來標(biāo)記一個閉包, 用法如下func hostFunc(@noescape closure: () -> ()) -> Void
@noescape字面意思是無法逃脫. closure 被@noescape修飾, 則聲明 closure 的生命周期不能超過 hostFunc, 并且, closure不能被hostFunc中的其他閉包捕獲(也就是強(qiáng)持有)

func hostFunc(@noescape closure: () -> ()) -> Void {
    //以下編譯出錯, closure 被修飾后, 不能被其他異步線程捕獲
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
        closure()
    }
}

5. Response

5.1 結(jié)構(gòu)體

在OC中猖吴,我們在寫代碼的時候很少用到結(jié)構(gòu)體。但是在Swift的中挥转,結(jié)構(gòu)體出現(xiàn)的頻率相當(dāng)高海蔽。在文件Response.swift中沒有定義一個類,全是結(jié)構(gòu)體绑谣。

先來回顧一下Swift中結(jié)構(gòu)體和類的關(guān)系:

a.都可以有屬性和方法党窜;
b.都有構(gòu)造器;
c.都支持附屬腳本借宵;
d.都支持?jǐn)U展刑然;
e.都支持協(xié)議。

然后我們來看看他們的不同之處:

a.類有繼承暇务;
b.結(jié)構(gòu)體有一個自動生成的逐一初始化構(gòu)造器泼掠;
c.在做賦值操作時,結(jié)構(gòu)體總是被拷貝(Array有特殊處理)垦细;
d.結(jié)構(gòu)體可以聲明靜態(tài)的屬性和方法择镇;
e.從設(shè)計模式的角度來分析,類的設(shè)計更側(cè)重于對功能的封裝括改,而結(jié)構(gòu)體的設(shè)計更側(cè)重于對數(shù)據(jù)的封裝腻豌。

類的設(shè)計更側(cè)重于對功能的封裝,而結(jié)構(gòu)體的設(shè)計更側(cè)重于對數(shù)據(jù)的封裝嘱能。為了便于代碼組織吝梅,一般在結(jié)構(gòu)體的擴(kuò)展里面添加方法,比如在Response.swift中:

public struct DefaultDataResponse {
    public let request: URLRequest?
    public let response: HTTPURLResponse?
    public let data: Data?
    public let error: Error?
    public let timeline: Timeline
    var _metrics: AnyObject?

    public init(
        request: URLRequest?,
        response: HTTPURLResponse?,
        data: Data?,
        error: Error?,
        timeline: Timeline = Timeline(),
        metrics: AnyObject? = nil)
    {
        self.request = request
        self.response = response
        self.data = data
        self.error = error
        self.timeline = timeline
    }
}

結(jié)構(gòu)體DefaultDataResponse完全滿足對數(shù)據(jù)的封裝惹骂,當(dāng)然這里用類來封裝這些數(shù)據(jù)其實也可以苏携,但是就感覺沒有那么完美。
同樣在文件中出現(xiàn)的DataResponse也是結(jié)構(gòu)體对粪。然后通過擴(kuò)展給結(jié)構(gòu)體添加方法右冻,或者應(yīng)該算是屬性装蓬。

5.2 Response協(xié)議

protocol Response {
    var _metrics: AnyObject? { get set }
    mutating func add(_ metrics: AnyObject?)
}

mutating:修飾方法是為了能在該方法中修改struct 或是 enum的變量,在設(shè)計接口的時候纱扭,也要考慮到使用者程序的擴(kuò)展性牍帚。所以要多考慮使用mutating來修飾方法。如果將Response中修飾方法的mutating去掉乳蛾,編譯器會報錯說沒有實現(xiàn)protocol暗赶。如果將struct中的mutating去掉,則會報錯不能改變結(jié)構(gòu)體的成員肃叶。

通過這樣定義之后蹂随,就可以讓結(jié)構(gòu)體實現(xiàn)這個協(xié)議,然后修改結(jié)構(gòu)體里面的變量了被环。

6. Result

這個類是一個泛型枚舉糙及,通過對結(jié)果的封裝可以直接獲取到更加詳細(xì)的信息。來看代碼:

public enum Result<Value> {
    case success(Value)
    case failure(Error)

    // 對結(jié)果信息進(jìn)一步處理筛欢,可以馬上返回成功或者失敗浸锨。
    public var isSuccess: Bool {
        switch self {
        case .success:
            return true
        case .failure:
            return false
        }
    }

    public var isFailure: Bool {
        return !isSuccess
    }

    /// 對結(jié)果信息進(jìn)一步處理,還可以直接返回成功的值版姑。
    public var value: Value? {
        switch self {
        case .success(let value):
            return value
        case .failure:
            return nil
        }
    }

    public var error: Error? {
        switch self {
        case .success:
            return nil
        case .failure(let error):
            return error
        }
    }
}
  • CustomStringConvertible柱搜,CustomDebugStringConvertible接口:這兩個接口都是自定義輸出的。之前如果要達(dá)到同樣的效果就重寫toString剥险。

7. SessionDelegate

SessionDelegate看名字以為它是一個代理聪蘸,其實它是一個類,前面說過類一般是對功能的封裝表制。這個類的作用是用閉包(也就是OC中的block)來替代系統(tǒng)中的代理回調(diào)健爬。大致分三個部分:

(1)聲明替代系統(tǒng)代理回調(diào)方法的閉包

注意這里定的閉包在下面的擴(kuò)展里面將對系統(tǒng)的代理進(jìn)行包裝一次,然后外面通過定義的閉包使用么介。

2017-06-15 下午3.22.57.png

(2)定義需要的屬性及方法娜遵。比如lock,sessionManager.

    var retrier: RequestRetrier?
    weak var sessionManager: SessionManager?

    private var requests: [Int: Request] = [:]
    private let lock = NSLock()

    /// Access the task delegate for the specified task in a thread-safe manner.
    open subscript(task: URLSessionTask) -> Request? {
        get {
            lock.lock() ; defer { lock.unlock() }
            return requests[task.taskIdentifier]
        }
        set {
            lock.lock() ; defer { lock.unlock() }
            requests[task.taskIdentifier] = newValue
        }
    }

(3)在類的擴(kuò)展里面實現(xiàn)系統(tǒng)代理,實現(xiàn)自定義閉包代替系統(tǒng)回調(diào)代理壤短。

這部分比較簡單设拟,基本思路是,實現(xiàn)代理久脯,在代理方法中調(diào)用定義好的閉包纳胧,傳遞參數(shù)。通過對系統(tǒng)的代理方法包裝一層帘撰,然后外部通過定義的閉包來調(diào)用跑慕。OC中的第三方BlockKit的底層原理和此類似。

2017-06-15 下午3.41.01.png

8. SessionManager

這個類非常重要骡和,包含以下8個方面:

(1)調(diào)用結(jié)果枚舉定義相赁。Helper

這個類似于在OC中定義成功回調(diào)和失敗回調(diào)相寇。只是現(xiàn)在把回調(diào)放到了枚舉里面慰于,這樣更加合理钮科。這種方式得益于case可以傳遞參數(shù)。

public enum MultipartFormDataEncodingResult {
    case success(request: UploadRequest, streamingFromDisk: Bool, streamFileURL: URL?)
    case failure(Error)
}

調(diào)用方式:

let encodingResult = MultipartFormDataEncodingResult.success(
                        request: self.upload(data, with: urlRequestWithContentType),
                        streamingFromDisk: false,
                        streamFileURL: nil
                    )

 DispatchQueue.main.async { encodingCompletion?(encodingResult) }

(2)屬性定義婆赠。Properties
屬性分為:計算屬性绵脯、存儲屬性、靜態(tài)屬性和實例屬性

靜態(tài)計算屬性:

open static let `default`: SessionManager = {
    let configuration = URLSessionConfiguration.default
    configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders

    return SessionManager(configuration: configuration)
}()

屬性的get/set方法

open var retrier: RequestRetrier? {
    get { return delegate.retrier }
    set { delegate.retrier = newValue }
}

這種方式類似于OC中重寫屬性的get/set方法休里。最常見的將model改變和UI綁定在一起蛆挫。

(3)生命周期。Lifecycle

Swift中屬性在初始化之后必須有值妙黍。
關(guān)于初始方法悴侵,Swift3中有init 和 init?,前者代碼一定會走的拭嫁,后者代表可能會走的初始化方法可免。

(4)數(shù)據(jù)請求。Data Request

這部分在定義方法上可以學(xué)習(xí)一下做粤,具體的內(nèi)容就是先定義一個最為基礎(chǔ)的方法浇借,參數(shù)比較多但是一定要有默認(rèn)值,然后后續(xù)的方法在參數(shù)上做減法怕品,最終都是調(diào)用最為基本的方法

(5)下載請求妇垢。Download Request
(6)上傳請求。Upload Request
(7)流式請求肉康。Stream Request
(8)重試闯估。Retry Request

9. TaskDelegate

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市吼和,隨后出現(xiàn)的幾起案子涨薪,更是在濱河造成了極大的恐慌李根,老刑警劉巖左权,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吐绵,死亡現(xiàn)場離奇詭異弓柱,居然都是意外死亡锻拘,警方通過查閱死者的電腦和手機(jī)绳瘟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進(jìn)店門焕蹄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來疫蔓,“玉大人塔粒,你說我怎么就攤上這事结借。” “怎么了卒茬?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵船老,是天一觀的道長咖熟。 經(jīng)常有香客問我,道長柳畔,這世上最難降的妖魔是什么馍管? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮薪韩,結(jié)果婚禮上确沸,老公的妹妹穿的比我還像新娘。我一直安慰自己俘陷,他們只是感情好罗捎,可當(dāng)我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拉盾,像睡著了一般桨菜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上捉偏,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天倒得,我揣著相機(jī)與錄音,去河邊找鬼告私。 笑死屎暇,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的驻粟。 我是一名探鬼主播根悼,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蜀撑!你這毒婦竟也來了挤巡?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤酷麦,失蹤者是張志新(化名)和其女友劉穎矿卑,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體沃饶,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡母廷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了糊肤。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片琴昆。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖馆揉,靈堂內(nèi)的尸體忽然破棺而出业舍,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布舷暮,位于F島的核電站态罪,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏下面。R本人自食惡果不足惜复颈,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望诸狭。 院中可真熱鬧券膀,春花似錦君纫、人聲如沸驯遇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叉庐。三九已至,卻和暖如春会喝,著一層夾襖步出監(jiān)牢的瞬間陡叠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工肢执, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留枉阵,地道東北人。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓预茄,卻偏偏與公主長得像兴溜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子耻陕,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容

  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young閱讀 3,802評論 1 10
  • 國家電網(wǎng)公司企業(yè)標(biāo)準(zhǔn)(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報批稿:20170802 前言: 排版 ...
    庭說閱讀 10,960評論 6 13
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理拙徽,服務(wù)發(fā)現(xiàn),斷路器诗宣,智...
    卡卡羅2017閱讀 134,652評論 18 139
  • 20- 枚舉,枚舉原始值,枚舉相關(guān)值,switch提取枚舉關(guān)聯(lián)值 Swift枚舉: Swift中的枚舉比OC中的枚...
    iOS_恒仔閱讀 2,278評論 1 6
  • 文/溫臺 是否夢有顏色 空白是打開夢境的鑰匙 我一直小心翼翼 收藏著 是否夢有顏色 抓著橫梯越過奇異風(fēng)景 我沒有驚...
    溫臺閱讀 176評論 6 1