弱引用对象
struct WeakObject<T: AnyObject>: Equatable, Hashable {
private let identifier: ObjectIdentifier
weak var object: T?
init(_ object: T) {
self.object = object
self.identifier = ObjectIdentifier(object)
}
var hashValue: Int {
return self.identifier.hashValue
}
static func == (lhs: WeakObject<T>, rhs: WeakObject<T>) -> Bool {
return lhs.identifier == rhs.identifier
}
}
弱引用数组
struct WeakObjectSet<T: AnyObject>: Sequence {
var objects: Set<WeakObject<T>>
init() {
self.objects = Set<WeakObject<T>>([])
}
init(_ object: T) {
self.objects = Set<WeakObject<T>>([WeakObject(object)])
}
init(_ objects: [T]) {
self.objects = Set<WeakObject<T>>(objects.map { WeakObject($0) })
}
var allObjects: [T] {
#if swift(>=4.1)
return objects.compactMap { $0.object }
#else
return objects.flatMap { $0.object }
#endif
}
func contains(_ object: T) -> Bool {
return self.objects.contains(WeakObject(object))
}
mutating func add(_ object: T) {
//prevent ObjectIdentifier be reused
if self.contains(object) {
self.remove(object)
}
self.objects.insert(WeakObject(object))
}
mutating func add(_ objects: [T]) {
objects.forEach { self.add($0) }
}
mutating func remove(_ object: T) {
self.objects.remove(WeakObject<T>(object))
}
mutating func remove(_ objects: [T]) {
objects.forEach { self.remove($0) }
}
// Sequence Protocol 只有一个必须要实现的方法makeIterator()
func makeIterator() -> AnyIterator<T> {
let objects = self.allObjects
var index = 0
// 需要返回一个 Iterator,它是一个 IteratorProtocol 类型
return AnyIterator {
defer { index += 1 }
return index < objects.count ? objects[index] : nil
}
}
}
Iterator 在 Swift 3.1 标准库中即为 IteratorProtocol,它用来为 Sequence 提供迭代能力。对于 Sequence,我们可以用for-in来迭代其中的元素,其实for-in的背后是 IteratorProtocol 在起作用
以下实现,与即时通讯接受消息实现类似,也可代替通知
public class Broadcaster {
/// 静态的可变字典,把协议名称作为key,把代理存放到对应的弱引用数组中
fileprivate static var observersDic = [String: Any]()
// Dispatch Barrier 解决多线程并发读写同一个资源发生死锁
// 需要注意 dispatch_barrier_async 只在自己创建的队列上有这种作用,在全局并发队列和串行队列上,效果和 dispatch_sync 一样
// 自定义全局队列
fileprivate static let notificationQueue = DispatchQueue(label: "com.swift.notification.center.dispatch.queue", attributes: .concurrent)
// 面向协议开发思路
public static func register<T>(_ protocolType: T.Type, observer: T) {
let key = "\(protocolType)"
safeSet(key: key, object: observer as AnyObject)
}
public static func unregister<T>(_ protocolType: T.Type, observer: T) {
let key = "\(protocolType)"
safeRemove(key: key, object: observer as AnyObject)
}
/// Remove all observers which comform to the protocol
public static func unregister<T>(_ protocolType: T.Type) {
let key = "\(protocolType)"
safeRemove(key: key)
}
public static func notify<T>(_ protocolType: T.Type, block: @escaping (T) -> Void ) {
let key = "\(protocolType)"
guard let objectSet = safeGetObjectSet(key: key) else {
return
}
// 此处执行大部分是UI界面刷新,因此要切换到UI线程
for observer in objectSet {
if let observer = observer as? T {
DispatchQueue.main.async {
block(observer)
}
}
}
}
}
private extension Broadcaster {
static func safeSet(key: String, object: AnyObject) {
notificationQueue.async(flags: .barrier) {
if var set = observersDic[key] as? WeakObjectSet<AnyObject> {
set.add(object)
observersDic[key] = set
}else{
observersDic[key] = WeakObjectSet(object)
}
}
}
static func safeRemove(key: String, object: AnyObject) {
notificationQueue.async(flags: .barrier) {
if var set = observersDic[key] as? WeakObjectSet<AnyObject> {
set.remove(object)
observersDic[key] = set
}
}
}
static func safeRemove(key: String) {
notificationQueue.async(flags: .barrier) {
observersDic.removeValue(forKey: key)
}
}
static func safeGetObjectSet(key: String) -> WeakObjectSet<AnyObject>? {
var objectSet: WeakObjectSet<AnyObject>?
// 同步执行 {number = 1, name = main}
// 如果当前线程是异步 {number = 3, name = (null)}
notificationQueue.sync {
// printLog(Thread.current)
objectSet = observersDic[key] as? WeakObjectSet<AnyObject>
}
return objectSet
}
}
与通知相比,传值非常方便
使用示例:
// 声明协议
protocol UpdateTitle {
func updateWithNewTitle(title: String)
}
override func viewDidLoad() {
super.viewDidLoad()
// 给对象注册协议
Broadcaster.register(UpdateTitle.self, observer: self)
}
// 实现协议
extension FirstViewController: UpdateTitle{
func updateWithNewTitle(title: String) {
titleLabel.text = title
}
}
// 其他地方的调用
@IBAction func updateTitle(sender: UIButton) {
Broadcaster.notify(UpdateTitle.self) {
printLog(Thread.current)
$0.updateWithNewTitle(title: self.textField.text ?? "")
}
}
总结:
- 面向协议开发思路
- 泛型应用示例
- 多线程
barrier
应用示例 T.Type
和.self
应用示例