美文网首页
【OC梳理】Copy、KVC、KVO

【OC梳理】Copy、KVC、KVO

作者: 忠橙_g | 来源:发表于2017-11-20 19:06 被阅读23次

    Copy

    OC中copy的作用是:利用一个源对象产生一个副本对象,它们之间不会相互影响。

    关于深拷贝与浅拷贝

    深拷贝是指对对象的具体内容进行复制,并占用新的内存空间,浅拷贝就是对内存地址的复制。
    自定义的类如果要深拷贝,需要遵循 NSCopying, NSMutableCopying 协议,在协议方法中实现copy相关方法。
    数组的深拷贝,也需要自己将所有对象拷贝一份再添加。
    下面的代码:

        NSMutableArray *array = [NSMutableArray array];
        [array addObject:[NSObject new]];
        [array addObject:[NSObject new]];
        [array addObject:[NSObject new]];
        [array addObject:[NSObject new]];
        NSArray *copyArray = [array copy];
        NSArray *copycopyArray = [copyArray copy];
        NSMutableArray *mutACopy = [array mutableCopy];
        NSMutableArray *arrCopy = [copyArray mutableCopy];
        NSLog(@"原来的数组            :%p ----> %p",&array,*&array);
        NSLog(@"copy的数组           :%p ----> %p",&copyArray,*&copyArray);
        NSLog(@"copycopy的数组       :%p ----> %p",&copycopyArray,*&copycopyArray);
        NSLog(@"mutableCopy原来的数组 :%p ----> %p",&mutACopy,*&mutACopy);
        NSLog(@"mutableCopy copy的数组:%p ----> %p",&arrCopy,*&arrCopy);
    

    输出的结果如下:

    ... Demo[11327:4606426] 原来的数组            :0x7ffee226d9a0 ----> 0x60400044a9e0
    ... Demo[11327:4606426] copy的数组           :0x7ffee226d998 ----> 0x60400044aa10
    ... Demo[11327:4606426] copycopy的数组       :0x7ffee226d990 ----> 0x60400044aa10
    ... Demo[11327:4606426] mutableCopy原来的数组 :0x7ffee226d988 ----> 0x60400044fcc0
    ... Demo[11327:4606426] mutableCopy copy的数组:0x7ffee226d980 ----> 0x60400044fcf0
    

    打断点可以看到所有的Array中的元素的地址都是相同的,并没有进行复制,并从输出结果看出:

    copy的对象为MutableArray时,会有一个新的指针指向新的内存地址(新的Array对象)。
    copy的对象为Array时,会有一个新的指针指向原来的内存地址(原来的Array对象)。
    mutableCopy的对象为MutableArray时,会有一个新的指针指向新的内存地址(新的MutableArray对象)。
    mutableCopy的对象为Array时,会有一个新的指针指向新的内存地址(新的MutableArray对象)。

    参考文章:
    Objective-C中的浅拷贝和深拷贝
    OC数组中的深拷贝

    KVC

    KVC(Key Valued Coding),键值编码,即常说的反射机制,是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性,进行属性的动态读写。
    对于某些private属性,如果使用KVC进行修改,这就破坏了类的封装性(当然了有些情况不得不使用KVC进行修改)。

    • KVC的主要用途是ORM映射,就是dictionary与model的互转。

    常用方法

    //获取值的方法
    valueForKey:            //传入NSString属性的名字。
    valueForKeyPath:        //传入NSString属性的路径,xx.xx形式。
    valueForUndefinedKey    //它的默认实现是抛出异常,可以重写这个函数做错误处理。
    
    修改值的方法
    setValue:forKey:
    setValue:forKeyPath:
    setValue:forUndefinedKey:
    

    使用KVC实现ORM

    假定有一个商品model,其定义属性如下:

    @property (nonatomic,copy) NSString * goodsId;      //商品ID
    @property (nonatomic,copy) NSString * coverImage;   //封面
    @property (nonatomic,copy) NSString * shopPrice;    //商城价(零售价)
    @property (nonatomic,copy) NSString * sales;        //销量
    //...
    

    服务器返回的字符串为:

    {
      "goods_id":"7",
      "cover_image":"http://www.nenyimall.com/products/H6200IFLO.jpg",
      "shop_price":"¥15.00",
      "sales":"139",
      ...
    }
    

    其中有3个字段的key与我们定义的属性名有区别,这时候,只需重写下面两个方法:

    -(void)setValue:(id)value forUndefinedKey:(NSString *)key{
        if ([key isEqualToString:@"goods_id"]) {
            [self setValue:value forKey:@"goodsId"];
        }else
        if ([key isEqualToString:@"cover_image"]) {
            [self setValue:value forKey:@"coverImage"];
        }else
        if ([key isEqualToString:@"shop_price"]) {
            [self setValue:value forKey:@"shopPrice"];
        }
    }
    
    -(id)valueForUndefinedKey:(NSString *)key{
        id result = nil;
        if ([key isEqualToString:@"goods_id"]) {
            result = [self valueForKey:@"goodsId"];
        }else
        if ([key isEqualToString:@"cover_image"]) {
            result = [self valueForKey:@"coverImage"];
        }else
        if ([key isEqualToString:@"shop_price"]) {
            result = [self valueForKey:@"shopPrice"];
        }
        return result;
    }
    

    然后就可以使用setValuesForKeysWithDictionary:将dictionary转换为model;
    使用dictionaryWithValuesForKeys:将属性转换为dictionary。
    参考文章:
    iOS开发-OC篇-KVC详解

    KVO

    KVO(Key Valued Observer),键值观察,是使用获取其他对象的特定属性变化的通知机制。所有NSObject的子类都支持这个机制。

    常用语法

    /**
     创建一个观察者
     @param observer 观察者
     @param keyPath 被观察的属性
     @param options 传递给接收者的值的类型 NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
     @param context 上下文,可用于区分注册者
     */
    [被观察对象 addObserver:<#(nonnull NSObject *)#> forKeyPath:<#(nonnull NSString *)#> options:<#(NSKeyValueObservingOptions)#> context:<#(nullable void *)#>];
    
    /**
     观察者的回调方法
     @param keyPath 被观察的属性
     @param object 被观察的属性值
     @param change 变化的记录
     @param context 上下文
     */
    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
      //do something
    }
    
    /**
     移除观察者
     @param observer 观察者
     @param keyPath 被观察的属性
     */
    [被观察对象 removeObserver:<#(nonnull NSObject *)#> forKeyPath:<#(nonnull NSString *)#>];
    

    局限性

    父类和子类同时存在KVO时(监听同一个对象的同一个属性),很容易出现对同一个keyPath进行两次removeObserver操作,从而导致程序crash。要避免这个问题,就需要区分出KVO是self注册的,还是superClass注册的,我们可以在 -addObserver:forKeyPath:options:context:和-removeObserver:forKeyPath:context这两个方法中传入不同的context进行区分。

    参考文章:
    OC中KVO的基本概念和使用方法
    iOS开发-KVO的奥秘
    iOS下KVO使用过程中的陷阱

    相关文章

      网友评论

          本文标题:【OC梳理】Copy、KVC、KVO

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