Swift写了一个简单的通知中心

1,742 阅读1分钟

一个可以简单应用的通知中心

  • 核心技术点就是使用了NSMapTableNSHashTable,据此可以使用weak的方式存储observer对象
  • 当observer销毁时,无需通过removeObserver方法移除观察者
import Foundation

class MyNotificationCenter {
    // MARK: - private property
    
    /// 存储通知名称和observer
    private lazy var observerData: NSMapTable<NSString, NSHashTable<AnyObject>> = NSMapTable.init(keyOptions: [.strongMemory, .objectPersonality], valueOptions: [.strongMemory, .objectPointerPersonality])
    /// 存储observer和selector
    private lazy var selectors: NSMapTable<AnyObject, NSDictionary> = NSMapTable.init(keyOptions: [.weakMemory, .objectPointerPersonality], valueOptions: [.strongMemory, .objectPersonality])
    
    private let sQueue = DispatchQueue(label: "MyNotificationCenterSerialQ")
    // MARK: - public property
    static let `default` = MyNotificationCenter()
    
    // MARK: - public function
    func addObserver(_ observer: AnyObject, selector: Selector, name: String) {
        sQueue.async {
            
            func addSelector() {
                if var sdict = self.selectors.object(forKey: observer) as? [String: String] {
                    if sdict[name] == nil {
                        sdict[name] = NSStringFromSelector(selector)
                    }
                    self.selectors.setObject(sdict as NSDictionary, forKey: observer)
                } else {
                    let dict = [name: NSStringFromSelector(selector)] as NSDictionary
                    self.selectors.setObject(dict, forKey: observer)
                    
                }
            }
            
            if let items = self.observerData.object(forKey: name as NSString) {
                if !items.contains(observer) {
                    items.add(observer)
                    self.observerData.setObject(items, forKey: name as NSString)
                    addSelector()
                }
            } else {
                let items = NSHashTable<AnyObject>.init(options: .weakMemory)
                items.add(observer)
                self.observerData.setObject(items, forKey: name as NSString)
                
                addSelector()
            }
        }
    }
    
    func post(name: String) {
        sQueue.sync {
            guard let items = self.observerData.object(forKey: name as NSString) else {
                return
            }
            DispatchQueue.main.async {
                for item in items.allObjects {
                    if let selectorDict = self.selectors.object(forKey: item) as? [String: String],
                       let selector = selectorDict[name] {
                        _ = item.perform(Selector(selector))
                    }
                }
            }
        }
    }
    
    // MARK: - private function
}