ios底层KVC分析(一)

12,509 阅读3分钟

分析入口

从OC代码追踪,发现KVC存在与Frameworks-CoreFoundation里,

image.png 但问题是苹果并未开放CoreFoundation源码,

从苹果官方文档入手developer.apple.com/library/arc…

image.png

About Key-Value Coding developer.apple.com/library/arc…

Key-value coding(键值编码)是一种间接访问properties的机制,通过NSKeyValueCoding非
正式协议支持。当一个对象遵循key-value coding时,它的properties可以通过一个简洁、统
一messaging接口进行寻址。较于实例变量及其关联访问,这种间接访问机制是对直接方式的扩充。

通常使用访问方法获取对象属性,get访问方法(或getter)返回属性的值,set访问方法(或
setter)设置属性的值。在Objective-C中,也可以直接访问属性的底层实例变量。以上任何一种
方式访问对象属性都很简单直接,但需要使用特定的属性方法或变量名才可以,lack flexibility 
(缺乏弹性)。对象属性列表是不断增长变化的,访问这些属性的方法也必须同步跟着变化。但不管
属性怎么变化,遵循键值编码的对象仅仅提供一个简单的messaging接口足够。

键值编码是许多其他Cocoa技术, 如key-value observing,Cocoa bindings, Core Data
和AppleScript-ability的基础概念,在某些情况下可以简化代码。

英文原文
Key-value coding is a mechanism enabled by the `NSKeyValueCoding` informal 
protocol that objects adopt to provide indirect access to their
properties. When an object is key-value coding compliant, its properties 
are addressable via string parameters through a concise, uniform messaging 
interface. This indirect access mechanism supplements the direct access 
afforded by instance variables and their associated accessor methods.

You typically use accessor methods to gain access to an object’s 
properties. A get accessor (or getter) returns the value of a property. A 
set accessor (or setter) sets the value of a property. In Objective-C, you 
can also directly access a property’s underlying instance variable. 
Accessing an object property in any of these ways is straightforward, but 
requires calling on a property-specific method or variable name. As the 
list of properties grows or changes, so also must the code which accesses 
these properties. In contrast, a key-value coding compliant object 
provides a simple messaging interface that is consistent across all of its 
properties.

Key-value coding is a fundamental concept that underlies many other Cocoa 
technologies, such as key-value observing, Cocoa bindings, Core Data, and 
AppleScript-ability. Key-value coding can also help to simplify your code 
in some cases.

KVC赋值&取值过程

Search Pattern for the Basic Setter

The default implementation of setValue:forKey:, given key and value parameters as input, attempts to set a property named key to value (or, for non-object properties, the unwrapped version of value, as described in Representing Non-Object Values) inside the object receiving the call, using the following procedure:

  1. Look for the first accessor named set<Key>: or _set<Key>, in that order. If found, invoke it with the input value (or unwrapped value, as needed) and finish.
  2. If no simple accessor is found, and if the class method accessInstanceVariablesDirectly returns YES, look for an instance variable with a name like _<key>_is<Key><key>, or is<Key>, in that order. If found, set the variable directly with the input value (or unwrapped value) and finish.
  3. Upon finding no accessor or instance variable, invoke setValue:forUndefinedKey:. This raises an exception by default, but a subclass of NSObject may provide key-specific behavior.

关键代码

image.png

    XXXPerson *p = [XXXPerson alloc];
    [p setValue:@"Ford" forKey:@"name"];
    int mcount = 0;
    Ivar *ivarList = class_copyIvarList([p class], &mcount);
    for (int i = 0; i < mcount; i++) {
        Ivar ivar = ivarList[i];
        char *ivarName = ivar_getName(ivar);
        id _objcIvar = object_getIvar(p, ivar);
        NSLog(@"------- ivarName:%s, ivarValue:%@", ivarName, _objcIvar);
    }
    NSLog(@"\n valueForKey:_name fetch result ==> %@, 
    _isName fetch result ==> %@, name fetch result ==> %@, 
    isName fetch result ==> %@ ",
          [p valueForKey:@"_name"], 
          [p valueForKey:@"_isName"], 
          [p valueForKey:@"name"], 
          [p valueForKey:@"isName"]);

    free(ivarList);

测试结果

image.png

valueForKey:_name fetch result ==> Ford, _isName fetch result ==> (null),
name fetch result ==> Ford, isName fetch result ==> (null)

KVC key-value 赋值顺序 通过 _name _isName name isName顺序进行,object valueForKey取值顺序严格按照 '_名字/名字'格式取出,也就是说通过有下划线的变量名或者变量名均可以取出值

如果实现了name的set方法 -- setName _setName setIsName OC set顺序为

  • setName
  • _setName
  • setIsName

如果实现了name的获取方法 -- getName name isName _name, OC 则取值顺序为

  • getName
  • name
  • isName
  • _name 如果不存在某个具体取值方法,则按顺序执行下一个进行代替