Alamofire源码学习目录合集swift
网络请求都会在异步完成,因此必定会碰到线程安全问题,须要在对某些共享数据读写时,考虑下多线程读写状况下的加解锁,原理很简单:只要在对线程安全敏感的数据进行读写操做时,加锁,读写结束后解锁便可,可是涉及到线程安全的数据不少,如何封装是个艺术问题~安全
Alamofire中定义了一个Protected类来当作属性包裹器使用,能够在对包裹的数据进行操做时进行加解锁操做,同时使用了Swift的语法特性与语法糖来让这个类功能很是强大。markdown
线程安全离不开锁,Alamofire框架同时支持Linux环境,所以定义了一个私有协议来表明锁对象,拥有加锁解锁功能,以后对锁进行了扩展,添加了线程安全的执行两个闭包,一个有一个泛型返回值,一个没有网络
private protocol Lock {
func lock()
func unlock()
}
extension Lock {
// 线程安全的执行闭包, 并把闭包的返回值返回给调用者
func around<T>(_ closure: () -> T) -> T {
lock(); defer { unlock() }
return closure()
}
// 线程安全的执行闭包
func around(_ closure: () -> Void) {
lock(); defer { unlock() }
closure()
}
}
复制代码
具体的锁类型,是根据运行环境来决定的,在Linux下,用的是pthread_mutex_t锁:多线程
#if os(Linux)
/// A `pthread_mutex_t` wrapper.
final class MutexLock: Lock {
private var mutex: UnsafeMutablePointer<pthread_mutex_t>
init() {
mutex = .allocate(capacity: 1)
var attr = pthread_mutexattr_t()
pthread_mutexattr_init(&attr)
pthread_mutexattr_settype(&attr, .init(PTHREAD_MUTEX_ERRORCHECK))
let error = pthread_mutex_init(mutex, &attr)
precondition(error == 0, "Failed to create pthread_mutex")
}
deinit {
let error = pthread_mutex_destroy(mutex)
precondition(error == 0, "Failed to destroy pthread_mutex")
}
fileprivate func lock() {
let error = pthread_mutex_lock(mutex)
precondition(error == 0, "Failed to lock pthread_mutex")
}
fileprivate func unlock() {
let error = pthread_mutex_unlock(mutex)
precondition(error == 0, "Failed to unlock pthread_mutex")
}
}
#endif
复制代码
在os环境下,用的是os_unfair_lock锁:闭包
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
/// An `os_unfair_lock` wrapper.
final class UnfairLock: Lock {
private let unfairLock: os_unfair_lock_t
init() {
unfairLock = .allocate(capacity: 1)
unfairLock.initialize(to: os_unfair_lock())
}
deinit {
unfairLock.deinitialize(count: 1)
unfairLock.deallocate()
}
fileprivate func lock() {
os_unfair_lock_lock(unfairLock)
}
fileprivate func unlock() {
os_unfair_lock_unlock(unfairLock)
}
}
#endif
复制代码
这个类官方的定义为:app
A thread-safe wrapper around a value.
线程安全的值包裹器框架
@propertyWrapper
@dynamicMemberLookup
final class Protected<T> {
// 锁
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
private let lock = UnfairLock()
#elseif os(Linux)
private let lock = MutexLock()
#endif
// 包裹的值
private var value: T
// 初始化方法, 供修饰属性赋值使用
/** 例: @Protected(3) var test: Int */
init(_ value: T) {
self.value = value
}
// 初始化方法, 修饰的属性有默认初始化值时调用
/** 例: @Protected var test: Int = 3 */
init(wrappedValue: T) {
value = wrappedValue
}
// propertyWrapper修饰必需要有的属性, 用来保存包裹的值, 这里定义为计算属性
// 在读写时, 使用锁来线程安全的执行闭包来对私有的value属性进行操做
var wrappedValue: T {
get { lock.around { value } }
set { lock.around { value = newValue } }
}
// projectedValue, Swift语法糖, 可使用$符号来获取
/** 例: class Test { @Protected var p: Int = 3 } let test = Test() let a = test.p // a类型为Int let b = test.$p // b类型为Protected<Int>类型, 这时候就可使用b来调用Protected类中定义的方法 b.read({ return String($0) //返回 "3" }) */
var projectedValue: Protected<T> { self }
//MARK: - 可供Protected调用的一些方法
// 同步的读取或者变换value值, 相似于map函数
func read<U>(_ closure: (T) -> U) -> U {
lock.around { closure(self.value) }
}
// 同步修改value值, 闭包入参为inout类型, 能够直接在调用write的时候进行修改
/** let c = b.write({ $0 = 2 }) 调用完成以后, b.value == 2, c == 2 */
@discardableResult
func write<U>(_ closure: (inout T) -> U) -> U {
lock.around { closure(&self.value) }
}
// dynamicMemberLookup修饰的必要方法, 对Protected类型使用.***获取属性时, 能够根据该方法来动态获取属性
// 好比Request中的: $mutableState.isFinishing
// mutableState的类型是被@Protected修饰的Request.MutableState
// 使用$mutableState获取到的是Protected<Request.MutableState>类型, 能够经过该方法来获取Request.MutableState类型的值
// $mutableState.isFinishing == mutableState.ifFinishing
subscript<Property>(dynamicMember keyPath: WritableKeyPath<T, Property>) -> Property {
get { lock.around { value[keyPath: keyPath] } }
set { lock.around { value[keyPath: keyPath] = newValue } }
}
}
复制代码
该类持有一个泛型value,能够用来包裹须要线程安全的值,当对该值进行读写时,须要经过该包裹器,先上锁在读写数据,而后解锁,借助Swift中的propertyWrapper属性包裹器修饰,能够很方便的实现包裹器逻辑异步
配合属性包裹器一块儿使用的,通常还有rejectedValue语法糖,由于使用@Protected包裹器修饰后的属性,若是使用self.***这样直接去获取属性的话,会直接获取到属性的类型,因此Alamofire又使用Swift中的projectedValue语法糖来把包裹器的原类型Protected给返回去,这样就能够调用包裹器中定义的方法,以及自由的选择获取包裹属性类型,仍是包裹器的类型async
还使用了dynamicMemberLookup修饰,用来在使用rejectedValue语法糖获取到Protected原类型的时候,经过动态查找属性的方法来获取所包裹的value的相关属性值,增长使用灵活性
只读写简单的基本值类型属性
class Test {
var unsafe: Int = 3
@Protected
var safe: Int = 3
func test() {
// 写入
self.unsafe = 2
self.safe = 2
self.$safe.write({
$0 = 2
})
//读取
let val1: Int = self.unsafe
let val2: Int = self.safe
let val3: Int = self.$safe.read({
$0
})
let val4: String = self.$safe.read({
String($0)
})
}
}
复制代码
Alamofire中主要的应用,是把一系列须要线程安全的值,定义为一个结构体,而后把整个结构体使用包裹器包裹,这样能够同时保证多个数据的线程安全。好比:
//在Request.swift中,Request类中定义
/// Type encapsulating all mutable state that may need to be accessed from anything other than the `underlyingQueue`.
struct MutableState {
var state: State = .initialized
...各类属性
var isFinishing = false
}
/// Protected `MutableState` value that provides thread-safe access to state values.
@Protected
fileprivate var mutableState = MutableState()
复制代码
后续使用时,普通读取数据的话就:
/// `State` of the `Request`.
public var state: State { mutableState.state }
/// Returns whether `state` is `.initialized`.
public var isInitialized: Bool { state == .initialized }
/// Returns whether `state is `.resumed`.
public var isResumed: Bool { state == .resumed }
复制代码
须要使用属性执行某些方法:
$mutableState.read { state in
//执行state中的请求建立成功回调
state.urlRequestHandler?.queue.async { state.urlRequestHandler?.handler(request) }
}
复制代码
写入数据:
$mutableState.write { $0.requests.append(adaptedRequest) }
复制代码
由于持有泛型value,因此Alamofire真对某些特殊类型的value作了一些额外的扩展,可使用$获取到Protected<T>原类型以后进行调用
// 当T是可变集合协议类型时, 添加快速追加元素的方法
extension Protected where T: RangeReplaceableCollection {
// 添加一个元素
func append(_ newElement: T.Element) {
write { (ward: inout T) in
ward.append(newElement)
}
}
// 从序列添加元素
func append<S: Sequence>(contentsOf newElements: S) where S.Element == T.Element {
write { (ward: inout T) in
ward.append(contentsOf: newElements)
}
}
// 从集合添加元素
func append<C: Collection>(contentsOf newElements: C) where C.Element == T.Element {
write { (ward: inout T) in
ward.append(contentsOf: newElements)
}
}
}
// 当T是可选Data类型时, 添加快速追加Data的方法
extension Protected where T == Data? {
// 追加Data
func append(_ data: Data) {
write { (ward: inout T) in
ward?.append(data)
}
}
}
//当T是Request.MutableState, 添加了两个工具方法
extension Protected where T == Request.MutableState {
// 线程安全地检测下可否改变成新状态
// 检测方法使用的是Request.MutableState本身的方法, 这里只是作了线程安全处理
func attemptToTransitionTo(_ state: Request.State) -> Bool {
lock.around {
guard value.state.canTransitionTo(state) else { return false }
value.state = state
return true
}
}
// 线程安全的使用当前state执行一个闭包
func withState(perform: (Request.State) -> Void) {
lock.around { perform(value.state) }
}
}
复制代码
有了这些扩展方法,Alamofire在内部使用的时候,就更加自由,不过惋惜这个方法被声明为了internal,集成Alamofire框架以后,无法使用Protected类,不过能够借鉴他的封装思路自行根据本身的需求进行封装处理
纯属我的理解, 可能存在理解错误的地方, 若有错误, 欢迎评论指出~ 感谢~