美文网首页
KVC实现流程(非原理苹果闭源看不到,只能根据实现流程猜测)

KVC实现流程(非原理苹果闭源看不到,只能根据实现流程猜测)

作者: 微笑_d797 | 来源:发表于2019-03-07 23:44 被阅读0次

成员变量 - 实例变量 - 属性
类里面声明的变量叫做成员变量,实例变量本质是成员变量,只是实例针对类而言,成员变量作用于类内部,无需与外界接触的变量,因为成员变量不会生成set、get方法所以外界无法与成员变量接触,根据成员变量四油性,为了方便访问就有了属性变量,属性变量的好处就是允许让其他对象访问到改变了,因为属性创建过程中自动生成了 -get -set 方法,当然也可以设置制度或者科协等,设置方法可以自定义,除去基本数据类型int float ....等,其他类型的变量都叫做实例变量。实例变量+基本数据类型变量=成员变量

KVC

打开官方文档官网对其定义是

KVC是由NSKeyValueCoding非正式协议实现的一种机制,对象采用该协议来提供对其属性的间接访问。当一个对象符合键值编码时,它的属性可以通过一个简洁、统一的消息传递接口通过字符串参数来寻址。这种间接访问机制补充了实例变量及其关联的访问方法所提供的直接访问。

NSKeyValueCoding

总结就是间接访问成员变量

取值过程

此处参考这里

image.png

1. 寻找 - (nullable id)valueForKey:(NSString *)key 的流程

Search the instance for the first accessor method found with a name like get<Key>, <key>, is<Key>, or _<key>, in that order. If found, invoke it and proceed to step 5 with the result. Otherwise proceed to the next step.
在实例中搜索找到的第一个访问器方法,其名称依次为get<key>、<key>、is<key>、_uukey>。如果找到,则调用它,并继续执行步骤5的结果。否则继续下一步。

/// Person.h
@interface Person : NSObject {
    NSString * name;
};

/// person.m
@implementation Person
    
-(void)_setName: (NSString *)name {
        
} 
#pragma mark get相关
-(NSString *)getName {
    NSLog(@"%s",__func__);
    return NSStringFromSelector(_cmd);
}
    
-(NSString *)name {
    NSLog(@"%s",__func__);
    return NSStringFromSelector(_cmd);
}
    
-(NSString *)_isname {
    NSLog(@"%s",__func__);
    return NSStringFromSelector(_cmd);
}
    
-(NSString *)_name {
    NSLog(@"%s",__func__);
    return NSStringFromSelector(_cmd);
}    
@end

///调用

    Person * person = [[Person alloc]init];
    [person setValue:@"2" forKey:@"name"];
    [person valueForKey:@"name"];

当调用 *- (nullable id)valueForKey:(NSString )key

image.png 得到调用顺序为 getname,name,isname,_name

所以当调用 *- (nullable id)valueForKey:(NSString )key会判断是否有响应的getkey方法,如果有会走到响应的地方。

2. 没有查到对应key的方法的处理

image.png

如果找不到简单的访问器方法,在实例中搜索名称与模式countof<key>和objectin<key>atindex(对应于nsarray类定义的基元方法)和<key>atindexes(对应于nsarray方法objectsatindexes:)匹配的方法。
如果找到了其中的第一个和另外两个中的至少一个,则创建一个集合代理对象,该对象响应所有NSarray方法并返回该对象。并执行第3步
代理对象随后将它接收的任何NSARRAY消息转换为countof<key>、objectin<key>atindex:、and<key>atindexes:messages的组合,转换为创建它的键值编码兼容对象。如果原始对象还实现了一个名为get<key>:range:的可选方法,则代理对象也会在适当时使用该方法。并执行第3步实际上,代理对象与键值编码兼容对象一起工作,允许基础属性的行为就好像它是一个NSarray,即使它不是。

如果找不到简单的访问器方法或数组访问方法组,请查找名为countof<key>、enumeratorf<key>和memberof<key>:的三种方法(对应于nsset类定义的基元方法)。如果找到这三个方法,则创建一个集合代理对象,该对象响应所有nsset方法并返回该对象。并执行第3步
此代理对象随后将其接收的任何nsset消息转换为countof<key>、enumeratorf<key>和memberof<key>:消息与创建它的对象的某种组合。实际上,与键值编码兼容对象一起工作的代理对象允许底层属性的行为就像它是一个nsset一样,即使它不是。

没有找到的话就是看他是不是实现数组或者集合的基类方法,实现了的话就会将他转为数组或者对象,此后该对象接受的任何与数组或者集合有关的方法都会以该数据类型接受。

3.判断 accessInstanceVariablesDirectly

image.png

如果找不到简单访问器方法或集合访问方法组,并且如果接收器的类方法accessInstanceVariablesDirectly返回Yes,则按该顺序搜索名为 _<key>、_is<key>、<key>或is<key>的实例变量。如果找到,直接获取实例变量的值,

如果检索到的属性值是对象指针,只需返回结果。
如果该值是nsnumber支持的对象类型则返回NSNUmber,否则返回NSValue

否则,调用valueForUndefinedKey:,默认情况下,这会引发异常,但NSObject的子类可能提供关键的特定行为

赋值过程

image.png

set value:for key:的默认实现:给定key和value参数作为输入,尝试在接收调用的对象内,使用以下过程将名为key的属性设置为value(对于非对象属性,则设置未包装的value版本,如表示非对象值:

按此顺序查找名为set<key>:或_set<key>的第一个访问器。如果找到了,使用输入值(或根据需要打开的值)调用它并完成。
如果找不到简单访问器,并且类方法accessInstanceVariablesDirectly返回Yes,则按该顺序查找名为 _<key>、_is<key>、<key>或is<key>的实例变量。如果找到,直接用输入值(或未包装的值)设置变量并完成。
在找不到访问器或实例变量时,调用setValue:ForUndefinedKey:。默认情况下,这会引发异常,但NSObject的子类可能提供关键的特定行为。

自定义KVC

简单的实现 仅仅基础类型的实现

@implementation NSObject(KVC)

- (void)jl_valueForKey:(NSString *)key {
  
}
    
- (void)jl_setValue:(id)value forKey:(NSString *)key {
    
    //1.如果key为nil 判断非空
    if (key == nil || key.length == 0) {
        return;
    }
    // 2.找到相关方法 -->顺序 set<Key> _set<Key> setIs<Key>
    NSString * Key = key.capitalizedString;
    NSString * setKey = [NSString stringWithFormat: @"setKey%@:",Key];
    NSString * _setKey = [NSString stringWithFormat: @"_setKey%@:",Key];
    
    if ([self performSelectorWithMethodName:setKey value:value]) {
        NSLog(@"***%@***",setKey);
        return;
    }
    if ([self performSelectorWithMethodName:_setKey value:value]) {
        NSLog(@"***%@***",_setKey);
    }
   
    if (![self.class accessInstanceVariablesDirectly]) {
        @throw [NSException exceptionWithName: @"UnKnowException" reason: [NSString stringWithFormat:@"[%@ valueForUnderfinedKey:]: this class is not key value coding-compliant for the key name.***)",self] userInfo:nil];
    }
    
    /// 获取成员变量 为某一个值赋值 -->顺序
    NSMutableArray * mArray = [self getIvarListName];
    NSString * _Key =  [NSString stringWithFormat: @"_%@:",Key];
    NSString * _isKey = [NSString stringWithFormat: @"_is%@:",Key];
    NSString * isKey = [NSString stringWithFormat: @"is%@:",Key];
    
    if ([mArray containsObject:_Key]) {
        Ivar ivar = class_getInstanceVariable([self class], _Key.UTF8String);
        object_setIvar(self, ivar, value);
        return;
    }
    if ([mArray containsObject:_isKey]) {
        Ivar ivar = class_getInstanceVariable([self class], _isKey.UTF8String);
        object_setIvar(self, ivar, value);
        return;
    }
    if ([mArray containsObject:Key]) {
        Ivar ivar = class_getInstanceVariable([self class], Key.UTF8String);
        object_setIvar(self, ivar, value);
        return;
    }
    if ([mArray containsObject:isKey]) {
        Ivar ivar = class_getInstanceVariable([self class], isKey.UTF8String);
        object_setIvar(self, ivar, value);
        return;
    }
    @throw [NSException exceptionWithName: @"UnKnowException" reason: [NSString stringWithFormat:@"[%@ valueForUnderfinedKey:]: this class is not key value coding-compliant for the key name.***)",self] userInfo:nil];
}

-(BOOL)performSelectorWithMethodName: (NSString *)methodName value:(id)value {
    
    if ([self respondsToSelector: NSSelectorFromString(methodName)]) {
        [self performSelector:NSSelectorFromString(methodName) withObject:value];
        return YES;
    }
    return NO;
}
    
-(id)performSelectorWithMethodName: (NSString *)methodName {
    if ([self respondsToSelector: NSSelectorFromString(methodName)]) {
        [self performSelector:NSSelectorFromString(methodName)];
    }
    return nil;
}
    
-(NSMutableArray *)getIvarListName {
    /// 获取所有成员变量的方法
    unsigned int outCount;
    Ivar * IvarArray = class_copyIvarList([self class], &outCount);
   
    NSMutableArray * ivararr = [[NSMutableArray alloc]init];
    
    for (unsigned i = 0; i < outCount; i ++) {
        Ivar * ivar = &IvarArray[i];
        NSLog(@"第%d个成员变量:%s,类型是:%s",i,ivar_getName(*ivar),ivar_getTypeEncoding(*ivar));
        [ivararr addObject: [NSString stringWithFormat:@"%@",ivar_getName(*ivar)]];
    }
    return ivararr;
}
    
@end

相关文章

网友评论

      本文标题:KVC实现流程(非原理苹果闭源看不到,只能根据实现流程猜测)

      本文链接:https://www.haomeiwen.com/subject/fjqkpqtx.html