• ios 深度复制 copy & mutablecopy


    首先讲一下
    copy 遵守NSCopying,并且实现 copyWithZone: 方法; 可以copy一个对象(OC已实现类,返回的是不可变对象,即使是NSMutableString和NSMutableArray,返回值 是NSString和NSArray这种)。

    mutableCopy 遵守NSMutableCopying,并且实现 mutableCopyWithZone: 方法 可以mutableCopy一个对象 (NSMutableString返回是 NSMutableString)。

    注意的一点是,不管是mutableCopy 还是 copy ,如果是NSArray里面的元素是对象的引用,那么复制出来的仍旧是引用。(简单的想,有一个数组A,里面的值是地址0x123,那么copy出来的数组,对应的值应该还是0x123.而0x123对应着一个对象的地址)

    那怎么做深度复制,把地址0x123对应的内存也复制过去?

    回想下,NSUserDefaults存放的数组,数组里面的对象的值,和 下次加载的时候对象的值是一样的,但是内存的地址是完全不一样的。

    那么,这样可以做到,完全复制!

    NSArray* newArr = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject: array]];
    其实就是一个序列化的过程。


    接下来,如果copy 或者 序列化的容器类或者对象类里面,含有自定义的类的时候,应该怎么办?

    1,copy 的问题。
    CustomClass 要实现 NSCopying 协议。

    - (id)copyWithZone:(NSZone *)zone;

    在上面的方法中,增加初始化和复制的过程。

    相当于 A = [B copy];调用的过程中,B会调用copyWithZone的函数,然后用自身的数据去分配和初始化 一个新的类,然后把这个类作为返回值Bnew, A = Bnew;

     

    2,序列化的问题。

    CustomClass 要实现 NSCoding 协议。

    - (void)encodeWithCoder:(NSCoder *)coder 函数中,把属性值一个个调用[coder encodeObject:tempValue forKey:propertyName];

    - (instancetype)initWithCoder:(NSCoder *)aDecoder 函数中,把属性值一个个[self setValue:[aDecoder decodeObjectForKey:propertyName] forKey:propertyName];

     

     

    这个两步都是一个繁琐的过程。

    然后就想一劳永逸下:

    @interface LYCoding : NSObject <NSCoding, NSCopying>

     

    @end

     

     

     

    @implementation LYCoding

     

    - (id)copyWithZone:(NSZone *)zone

    {

        id ret = [[[self class] allocWithZone:zone] init];

        

        unsigned int propertyCount = 0;

        objc_property_t * properties = class_copyPropertyList( [self class], &propertyCount );

        

        for ( NSUInteger i = 0; i < propertyCount; i++ )

        {

            const char * name = property_getName(properties[i]);

            NSString * propertyName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];

            

            NSObject<NSCopying> * tempValue = [self valueForKey:propertyName];

            if (tempValue) {

                id value = [tempValue copy];

                [ret setValue:value forKey:propertyName];

            }

        }

     

        return ret;

    }

     

     

    - (void)encodeWithCoder:(NSCoder *)coder

    {

        unsigned int propertyCount = 0;

        objc_property_t * properties = class_copyPropertyList( [self class], &propertyCount );

        

        for ( NSUInteger i = 0; i < propertyCount; i++ )

        {

            const char * name = property_getName(properties[i]);

            NSString * propertyName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];

            

            NSObject * tempValue = [self valueForKey:propertyName];

         //   [tempValue conformsToProtocol:@protocol(NSCoding)];

            [coder encodeObject:tempValue forKey:propertyName];

        }

    }

     

    - (instancetype)initWithCoder:(NSCoder *)aDecoder

    {

        if (self = [super init]) {

            unsigned int propertyCount = 0;

            objc_property_t * properties = class_copyPropertyList( [self class], &propertyCount );

            

            for ( NSUInteger i = 0; i < propertyCount; i++ )

            {

                const char * name = property_getName(properties[i]);

                NSString * propertyName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];

                

                [self setValue:[aDecoder decodeObjectForKey:propertyName] forKey:propertyName];

            }

        }

        

        return self;

    }

     

    @end

    这个是我自定义的基类,实现copy 和 coding的协议。新的类如果需要就直接继承这个类。

    [tempValue conformsToProtocol:@protocol(NSCoding)];

    这一行代码有什么用?

     

    因为这个类还不完善,如果CustomA类中,有一个属性值是CustomB类。

    那么需要在encode A类的时候,判断下每个值 的属性,是否实现了NSCoding,如果是,那么把它序列化成NSData.(NSData *personEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:item];)

     

    因为在这个项目,还用不到这个东西,所以就没加上去。 

     

  • 相关阅读:
    中介模式与外观模式(门面模式)区别
    java反射
    Spring注解@ResponseBody,@RequestBody
    Spring事务管理
    Junit运行在Spring环境下
    java开发常用到的jar包总结
    java二维数组
    Android开发之执行定时任务AlarmManager,Timer,Thread
    Android开发之Android Context,上下文(Activity Context, Application Context)
    Android开发之创建App Widget和更新Widget内容
  • 原文地址:https://www.cnblogs.com/loying/p/4862275.html
Copyright © 2020-2023  润新知