KVC原理
由于使用的是Swift语言,定义的类属性需要加@objc,保证属性添加到运行时方法 如下图: KVC的内部实现流程,可阅读苹果文档,主要步骤如下,原文翻译的,不清楚可以点击文档查看 取值 value(forKey key: String) 1.valueForKey:的默认实现将一个键参数作为输入,执行以下过程,在接收valueForKey:调用的类实例中进行操作。 在实例中搜索找到的第一个访问器方法,该方法的名称依次为getKey、、is或。如果找到,调用它并继续执行步骤5,并显示结果。否则继续下一步。
2.如果找不到简单的访问器方法,请在实例中搜索名称与模式countOfKey和objectInKeyAtIndex:(对应于NSArray类定义的基本方法)和KeyAtIndex:(对应于NSArray方法objectsatinexes:)匹配的方法。 如果找到了其中的第一个以及其他两个方法中的至少一个,则创建一个集合代理对象,该对象响应所有NSArray方法并返回该对象。否则,继续执行步骤3。 代理对象随后将其接收到的任何NSArray消息转换为countOfKey、objectInKeyAtIndex:、和KeyAtIndex:消息的组合,并转换为创建它的键值编码兼容对象。如果原始对象还实现了一个名为getKey:range:的可选方法,那么代理对象也会在适当的时候使用该方法。实际上,代理对象与键值编码兼容对象一起工作,允许基础属性的行为如同NSArray一样,即使它不是。
3.如果找不到简单的访问器方法或数组访问方法组,请查找名为countOfKey、EnumeratorfKey和memberOfKey:(对应于NSSet类定义的基本方法)的三个方法。 如果找到这三个方法,则创建一个集合代理对象,该对象响应所有NSSet方法并返回该对象。否则,继续执行步骤4。 此代理对象随后将其接收到的任何NSSet消息转换为countOfKey、enumeratorOfKey和memberOfKey:消息的某种组合,并发送给创建它的对象。实际上,代理对象与键值编码兼容对象一起工作,允许基础属性的行为如同NSSet一样,即使它不是NSSet。
4.如果未找到简单的访问器方法或集合访问方法组,并且如果接收方的类方法AccessInstanceVariables直接返回YES,则按该顺序搜索名为key、iskey、key或iskey的实例变量。如果找到,直接获取实例变量的值并继续执行步骤5。否则,继续执行步骤6。
5.如果检索到的属性值是对象指针,只需返回结果。 如果该值是NSNumber支持的标量类型,请将其存储在NSNumber实例中并返回该值。 如果结果是NSNumber不支持的标量类型,请转换为NSValue对象并返回该对象。
6.如果所有其他操作都失败,请调用valueForUndefinedKey:。默认情况下,这会引发异常
2.设置值 setValue:forKey:
1.查找名为setKey:或_setKey的第一个访问器。如果找到,则使用输入值(或根据需要取消包装的值)调用它并完成。 如果找不到简单访问器,并且如果类方法AccessInstanceVariables直接返回YES,请按顺序查找名为“key、\u iskey、key或iskey”的实例变量。如果找到,直接用输入值(或展开值)设置变量并完成。 在找不到访问器或实例变量时,调用setValue:forUndefinedKey:。默认情况下,这会引发异常,但NSObject的子类可能会提供特定于键的行为。
KVC原理流程差不多了解清楚了,还是要自己敲代码,否者纸上谈兵.
1.自定义KVC //自定义KVC func xr_setValue(_ value: Any?, forkey key:String) throws { if key.count==0 { return }
let Key = key.capitalized //首字母大写
let func1 = String(format: "set%@", Key)
let func2 = String(format: "_set%@", Key)
if responds(to: Selector(func1)) {
perform(Selector(func1), with: value)
return
}
else if responds(to: Selector(func2))
{
perform(Selector(func2), with: value)
return
}
/*
如果上述方法都没有实现 要看accessInstanceVariablesDirectly 是否返回YES
返回No,抛出异常,为YES就继续寻找 key
*/
if !(self.self as! AnyClass).accessInstanceVariablesDirectly {
throw NSException(name: NSExceptionName("XRUnknownKeyException"), reason: String(format:"[%@ valueForUndefinedKey:]: this class is not key value coding-compliant for the key name %@",self,key), userInfo: nil) as! Error
}
let marray = getIvarListName()
let _key = String(format: "_%@", key)
let _isKey = String(format: "_is%@", Key)
let isKey = String(format: "is%@", Key)
if marray.contains(_key) {
let ivar = class_getInstanceVariable(self.self as! AnyClass,(_key as NSString).utf8String!)
object_setIvar(self, ivar!, value)
return
}
else if marray.contains(_isKey)
{
let ivar = class_getInstanceVariable(self.self as! AnyClass,(_isKey as NSString).utf8String!)
object_setIvar(self, ivar!, value)
return
}
else if marray.contains(isKey)
{
let ivar = class_getInstanceVariable(self.self as! AnyClass,(isKey as NSString).utf8String!)
object_setIvar(self, ivar!, value)
return
}
throw NSException(name: NSExceptionName("XRUnknownKeyException"), reason: String(format:"[%@ valueForUndefinedKey:]: this class is not key value coding-compliant for the key name %@",self,key), userInfo: nil) as! Error
}
func getIvarListName()->Array<String>
{
var marray:Array<String>=[]
let ivars = class_copyIvarList(self.self as! AnyClass, &count) // class_copyIvarList
for i in 0..<count
{
let ivar = ivars![Int(i)]
//获取属性名称
let name = ivar_getName(ivar)
let key = String(cString: name!)
marray.append(key)
}
free(ivars)
return marray
}
func xr_value(forKey key: String) throws -> Any? {
if key.count == 0
{
return nil;
}
let Key = key.capitalized
let getKey = String(format: "get%@", Key)
let countOfKey = String(format: "countOf%@", Key)
let objectInkeyAtIndex = String(format: "objectIn%@AtIndex", Key)
if responds(to: Selector(getKey))
{
perform(Selector(getKey))
}
else if responds(to: Selector(key))
{
perform(Selector(key))
}
else if responds(to: Selector(countOfKey))
{
if responds(to: Selector(objectInkeyAtIndex))
{
let num = perform(Selector(countOfKey)) as! Int
var mArray:Array<Any>?
for i in 0..<num {
mArray?.append(perform(Selector(objectInkeyAtIndex)))
}
return mArray
}
}
//判断是否能够直接赋值实例变量
if !NSObject.accessInstanceVariablesDirectly {
throw NSException(name: NSExceptionName("XRUnknownKeyException"), reason: String(format:"[%@ valueForUndefinedKey:]: this class is not key value coding-compliant for the key name %@",self,key), userInfo: nil) as! Error
}
// 4.找相关实例变量进行赋值
let marray = getIvarListName()
let _key = String(format: "_%@", key)
let _isKey = String(format: "_is%@", Key)
let isKey = String(format: "is%@", Key)
if marray.contains(_key) {
let ivar = class_getInstanceVariable(self.self as! AnyClass,(_key as NSString).utf8String!)
return object_getIvar(self, ivar!)
}
else if marray.contains(_isKey)
{
let ivar = class_getInstanceVariable(self.self as! AnyClass,(_isKey as NSString).utf8String!)
return object_getIvar(self, ivar!)
}
else if marray.contains(isKey)
{
let ivar = class_getInstanceVariable(self.self as! AnyClass,(isKey as NSString).utf8String!)
return object_getIvar(self, ivar!)
}
return ""
}
|