Alomofire源码解析1-Protected

305 阅读1分钟

功能

保证成员变量的存取线程安全。

知识点

  1. propertyWrapper

  2. dynamicMemberLookup

  3. defer

功能实现

简单的锁封装

extension NSLock {
    func around<T>(_ closure: () throws -> T) rethrows -> T {
        lock(); defer { unlock() }
        
        return try closure()
    }
    
    func around(_ closure: () throws -> Void) rethrows {
        lock(); defer { unlock() }
        
        try closure()
    }
    
    func around(_ closure: () -> Void) {
        lock(); defer { unlock() }
        closure()
    }
}

这样封装之后,可以屏蔽加锁的细节代码

lock()
someCode()
unLock()

变为

lock.around {
    someCode()
}

成员变量的锁机制

Objc中可以使用atomic,但是Swift就比较麻烦

var _lockName = ""
var lockName: String {
        get {
            return lock.around {
                self._lockName
            }
        }
        
        set {
            lock.around {
                self._lockName = newValue
            }
        }
    }

这样的代码复用性极差,又臭又长

利用propertyWrapper实现加锁

简单的存取加锁

@propertyWrapper
class Protected<T> {
    var lock = NSLock()
    var value: T
    
    var wrappedValue: T {
        get {
            lock.around {
                self.value
            }
        }
        
        set {
            lock.around {
                value = newValue
            }
        }
    }
    
    init(wrappedValue: T) {
        self.value = wrappedValue
    }
}

这样我们就可以方便的保证成员变量的线程安全性了,如下

class Person {
    @Protected
    var name = Name()
}

存取上下文加锁

只实现针对变量的存取加锁,并不满足所有场景。

通过propertyWrapper的projectedValue可以实现存取的上下文加锁

@propertyWrapper
class Protected<T> {
    var lock = NSLock()
    var value: T
    
    var projectValue: Protected<T> { self }
    
    // ....
    
    
    func read<U>(_ closure: (T) throws -> U) rethrows -> U {
        try lock.around {
            try closure(self.value)
        }
    }
}

使用,如下

let p = Person()
p.$name.read { name in
    someCode()
}

这样就可以保证整个闭包针对这个成员变量的使用都是线程安全的。

利用dynamicMemberLookup来保证加锁对象的成员变量线程安全

比如

@Protected
var person: Person // 如果保证我再使用name 和 age的时候也是线程安全的

实现如下

@propertyWrapper
@dynamicMemberLookup
class Protected<T> {
    // ....
    subscript<Property>(dynamicMember keyPath: WritableKeyPath<T, Property>) -> Property {
        get { lock.around { value[keyPath: keyPath] } }
        set { lock.around { value[keyPath: keyPath] = newValue } }
    }

    subscript<Property>(dynamicMember keyPath: KeyPath<T, Property>) -> Property {
        lock.around { value[keyPath: keyPath] }
    }
}

利用dynamicMemberLookup 针对keypath的读写进行加锁.

// 这样不仅可以保证加锁对象是安全的,其对象的存取也是加锁的
/// lock() 
/// person.name
/// unlock()
$person.name