四川網(wǎng)站建設(shè)套餐友情鏈接交易網(wǎng)
KVC
KVC意思是鍵值編碼,是一種可以通過鍵名來訪問對象屬性的機制,也可以對屬性進行賦值,包括私有屬性,由于KVC的定義是對OC中的NSObject的擴展進行實現(xiàn)的,所以如果要使用KVC機制,那么這個類需要繼承NSObject,同時屬性需添加@objc關(guān)鍵字。如圖:
class Person:NSObject{@objc var name:String{set{_name = newValueprint("name被賦值了")}get{print("name被訪問了")return _name}}@objc var age:Intinit(age: Int) {self.age = age}var _name = ""
}
在上面的示例中,name和age變量的用了@objc修飾,所以可以通過KVC機制對這兩個變量進行讀寫,而_name沒有@objc關(guān)鍵字修飾,則不能通過KVC機制進行讀寫。
使用KVC機制進行讀寫訪問:
let p = Person(age: 20)
p.setValue("Ally", forKey: "name")//使用KVC進行賦值操作
let pName = p.value(forKey: "name") as! String//使用KVC進行訪問操作
print(pName)
在使用KVC對計算屬性(上面示例中的name屬性)進行讀寫操作時,會分別調(diào)用屬性的getter和setter方法。
執(zhí)行結(jié)果如圖:
補充:
計算屬性:在Swift中,計算屬性不直接存儲值,而是提供一個 getter 和一個可選的 setter 來間接獲取和設(shè)置其他屬性的值。計算屬性可以不需要初始化,因為計算屬性并不會占用內(nèi)存。
KVC機制和.語法的區(qū)別:
我們知道,對類的屬性進行讀寫時,可以創(chuàng)建對象,然后通過.語法對屬性進行讀寫操作,那么兩者的區(qū)別是什么呢?
對于KVC機制,它是在運行時才會檢查錯誤,比如訪問某個不存在的屬性安全性較低,而點語法在編譯時會進行類型檢查,同時.語法不能訪問或修改私有成員變量,而KVC機制可以。
KVC賦值和取值的原理:
賦值:
- 優(yōu)先查找set<Key>方法或_set<Key>的第一個訪問器。找到則直接完成。
- 如果沒有找到訪問器,則會檢查類方法
accessInstanceVariablesDirectly
是否為YES,如果是YES則會查找
名稱為_< key >、_is< key >、< key >或is< key >的實例變量,。如果找到,則直接完成。 - 如果沒有訪問器或?qū)嵗兞繒r,調(diào)用
setValue:forUndefinedKey:
。這將在默認(rèn)情況下引發(fā)異常。
取值:
首先查找get<Key>、<Key>、is<Key>或_<Key>的方法,找到則直接調(diào)用。???????
如果都沒找到,KVC則會查找countOf<Key>,objectIn<Key>AtIndex或<Key>AtIndexes格式的方法。如果countOf<Key>方法和另外兩個方法中的一個被找到,那么就會返回一個可以響應(yīng)NSArray所有方法的代理集合(它是NSKeyValueArray,是NSArray的子類),調(diào)用這個代理集合的方法,或者說給這個代理集合發(fā)送屬于NSArray的方法,就會以countOf<Key>,objectIn<Key>AtIndex或<Key>AtIndexes這幾個方法組合的形式調(diào)用。還有一個可選的get<Key>:range:方法。所以你想重新定義KVC的一些功能,你可以添加這些方法,需要注意的是你的方法名要符合KVC的標(biāo)準(zhǔn)命名方法,包括方法簽名。
如果上面的方法沒有找到,那么會同時查找countOf<Key>,enumeratorOf<Key>,memberOf<Key>格式的方法。如果這三個方法都找到,那么就返回一個可以響應(yīng)NSSet所的方法的代理集合,和上面一樣,給這個代理集合發(fā)NSSet的消息,就會以countOf<Key>,enumeratorOf<Key>,memberOf<Key>組合的形式調(diào)用。
如果還沒有找到,再檢查類方法+ (BOOL)accessInstanceVariablesDirectly,如果返回YES(默認(rèn)行為),那么和先前的設(shè)值一樣,會按_<key>,_is<Key>,<key>,is<Key>的順序搜索成員變量名。如果重寫了類方法+ (BOOL)accessInstanceVariablesDirectly返回NO的話,那么會直接調(diào)用valueForUndefinedKey:
還沒有找到的話,調(diào)用valueForUndefinedKey:
KVC在實際應(yīng)用中可以字典轉(zhuǎn)模型,如:
let p = Person(age: 20)
let dict = ["name":"BBB","age":30,"_name":"_name"] as [String : Any]p.setValuesForKeys(dict)
print(p.name)
print(p.age)
print(p._name)
運行結(jié)果:
KVO
KVO意思是鍵值觀察,是基于KVC以及動態(tài)派發(fā)技術(shù)實現(xiàn)的,它是觀察者模式的一種衍生,其思想是:當(dāng)被觀察對象的某個屬性發(fā)生變化時,另一個類可以通過觀察獲取通知,并做出處理。
KVO是對NSObject的擴展來實現(xiàn)的,當(dāng)使用KVO時,需要繼承NSObject,并且觀察屬性需要添加@objc dynamic標(biāo)識,如:
class Person:NSObject{@objc dynamic var name:String{set{_name = newValueprint("name被賦值了")}get{print("name被訪問了")return _name}}@objc var age:Intinit(age: Int) {self.age = age}@objc var _name = ""deinit {removeObserver(self, forKeyPath: "name")}}
注意:observer對資源消耗大,建議在類銷毀的時候移除觀察者,如上示例中的析構(gòu)deinit()中.
接著我們定義一個Model(B類),負(fù)責(zé)監(jiān)聽A類中的屬性name,如:
class Model:NSObject{let p2 = Person(age: 30)override init(){super.init()p2.addObserver(self as! NSObject, forKeyPath: "name", options: .new, context: nil)}//響應(yīng)回調(diào)事件的重寫,需要繼承NSObjectoverride func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {print(22)if p2.name != ""{print("p2`s name is changed,is \(p2.name)")}}}
??????????????注意:你需要繼承NSObject類,從而對響應(yīng)回調(diào)事件進行重寫。
接著我們創(chuàng)建實例,修改p2的name值:
let model = Model()
model.p2.name = "good"
當(dāng)p2.name值被修改后,會調(diào)用響應(yīng)事件,運行結(jié)果如下:
KVO在iOS實際開發(fā)中的舉例:
import UIKit
import Foundationclass Persons:NSObject {@objc dynamic var name: String?init(name: String? = nil) {self.name = name}
}class ViewController: UIViewController {//實例化被觀察對象var person = Persons(name: "Aliy")override func viewDidLoad() {super.viewDidLoad()self.view.backgroundColor = .red//添加觀察者self.person.addObserver(self, forKeyPath: "name", options: .new, context: nil)//3s后執(zhí)行被觀察對象值的改變self.perform(#selector(ChangeName), with: nil, afterDelay: 3.0)}override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {if person.name != "" {print("The new person is \(person.name!)")self.view.backgroundColor = . green}}}extension ViewController {@objc func ChangeName(){person.name = "John"}
}
這里注意:ViewController不需要再去繼承NSObject類,因為它已經(jīng)繼承了UIViewController,而Swift 中的?UIViewController
?繼承自?UIResponder
,而?UIResponder
繼承自?NSObject
?類。