KVO是Key Value Observer的縮寫,可以用於監視一個物件的屬性值變化,然後執行一個程式碼塊(塊、函式、閉包等)。Facebook開源了一個KVO框架,KVOController。
這個案例使用KVOController,用於App主題改變通知。通過KVO監視主題的值,當設定主題變化時,通知所有的UI類的主題處理器。程式碼如下:
import UIKit
import KVOController
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow();
self.window?.frame=UIScreen.main.bounds;
self.window?.makeKeyAndVisible();
self.window?.rootViewController = Page()
return true
}
}
class Page: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton(type: .system)
button.frame = CGRect(x: 120, y: 150, width: 220, height: 50)
button.contentHorizontalAlignment = .left
button.setTitle("Peace",for: .normal)
button.addTarget(self, action: #selector(buttonAction(_:)), for: .touchUpInside)
view.addSubview(button)
let button1 = UIButton(type: .system)
button1.frame = CGRect(x: 120, y: 200, width: 220, height: 50)
button1.contentHorizontalAlignment = .left
button1.setTitle("Blood",for: .normal)
button1.addTarget(self, action: #selector(buttonAction1(_:)), for: .touchUpInside)
view.addSubview(button1)
self.themeChangedHandler = {[weak self] (style) -> Void in
if "Peace" == style{
self!.view.backgroundColor = .blue
}
else{
self!.view.backgroundColor = .red
}
}
}
func buttonAction(_ sender:UIButton!){
ThemeColor.shared.style = "Peace"
}
func buttonAction1(_ sender:UIButton!){
ThemeColor.shared.style = "Blood"
}
}
class ThemeColor :NSObject {
dynamic var style:String
static let shared = ThemeColor()
fileprivate override init(){
style = "Blood"// or Peace
super.init()
}
}
extension NSObject {
fileprivate struct AssociatedKeys {
static var themeChanged = "themeChanged"
}
public typealias ThemeChangedClosure = @convention(block) (_ style:String) -> Void
var themeChangedHandler:ThemeChangedClosure? {
get {
let closureObject: AnyObject? = objc_getAssociatedObject(self, &AssociatedKeys.themeChanged) as AnyObject?
guard closureObject != nil else{
return nil
}
let closure = unsafeBitCast(closureObject, to: ThemeChangedClosure.self)
return closure
}
set{
guard let value = newValue else{
return
}
let dealObject: AnyObject = unsafeBitCast(value, to: AnyObject.self)
objc_setAssociatedObject(self, &AssociatedKeys.themeChanged,dealObject,objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
self.kvoController.observe(ThemeColor.shared, keyPath: "style", options: [.initial,.new] , block: {[weak self] (nav, color, change) -> Void in
self?.themeChangedHandler?(ThemeColor.shared.style)
}
)
}
}
}複製程式碼
程式碼執行後,可以看到兩個按鈕,點選不同的按鈕會切換主題。
Pod檔案為:
pod `KVOController`, `~> 1.2.0`複製程式碼
程式碼的效果就是,當修改:
ThemeColor.shared.style = "Peace"複製程式碼
就會引發當前物件的塊物件:themeChangedHandler的呼叫。因為使用了KVO監視了ThemeColor.shared.style,當值變化的時候,就會引發themeChangedHandler所指向的閉包的執行。閉包在set的時候被使用objc_setAssociatedObject儲存於指定鍵(themeChanged)的值內。當執行get的時候,會使用objc_getAssociatedObject函式把它再取出來。
函式對:objc_getAssociatedObject和objc_setAssociatedObject看起來比較複雜,但是可以用於動態的為物件建立屬性,因此頗為強大。
此程式碼不好理解,也許使用Observer設計模式來替代更好。