iOS - OC Copy 拷贝

简介: 前言copy:需要先实现 NSCopying 协议,创建的是不可变副本。mutableCopy:需要实现 NSMutableCopying 协议,创建的是可变副本。浅拷贝:指针拷贝,源对象和副本指向的是同一个对象。

前言

  • copy:需要先实现 NSCopying 协议,创建的是不可变副本。

  • mutableCopy:需要实现 NSMutableCopying 协议,创建的是可变副本。

  • 浅拷贝:指针拷贝,源对象和副本指向的是同一个对象。对象的引用计数器 +1,其实相当于做了一次 retain 操作。

  • 深拷贝:内容拷贝,源对象和副本指向的是两个不同的对象。源对象引用计数器不变,副本计数器设置为 1。

  • 只有不可变对象创建不可变副本(copy)才是浅拷贝,其它都是深拷贝。

  • 在 iOS 中并不是所有的对象都支持 copy,mutableCopy,遵守 NSCopying 协议的类可以发送 copy 消息,遵守 NSMutableCopying 协议的类才可以发送 mutableCopy 消息。假如发送了一个没有遵守上 诉两协议而发送 copy 或者 mutableCopy,那么就会发生异常。但是默认的 iOS 类并没有遵守这两个协议。如果想自定义一下 copy 那么就必须遵守 NSCopying,并且实现 copyWithZone: 方法,如果想自定义一下 mutableCopy 那么就必须遵守 NSMutableCopying,并且实现 mutableCopyWithZone: 方法。

  • copy 是创建一个新对象,retain 是创建一个指针,引用对象计数加 1。copy 属性表示两个对象内容相同,新的对象 retain 为 1 ,与旧有对象的引用计数无关,旧有对象没有变化。copy 减少对象对上下文的依赖。retain 属性表示两个对象地址相同(建立一个指针,指针拷贝),内容当然相同,这个对象的 retain 值 +1 也就是说,retain 是指针拷贝,copy 是内容拷贝。

1、系统的非容器类对象

    /*
        系统的非容器类对象指的是 NSString,NSNumber 等等一类的对象。
 
            对于系统的非容器类对象,如果对一不可变对象复制,copy 是指针复制(浅拷贝),mutableCopy 就是对象复制(深拷贝)。
        如果是对可变对象复制,都是深拷贝,但是 copy 返回的对象是不可变的。
    */

    NSString *string = @"origion";
        
    NSString *stringCopy = [string copy];
    NSMutableString *mstringMCopy = [string mutableCopy];
        
    NSLog(@"%p", string);
    NSLog(@"%p", stringCopy);                       // 地址与 string 相同
    NSLog(@"%p", mstringMCopy);                     // 地址与 string 不同
        
    NSLog(@"%zi", [string retainCount]);            // 引用计数为 2
    NSLog(@"%zi", [stringCopy retainCount]);        // 引用计数为 2
    NSLog(@"%zi", [mstringMCopy retainCount]);      // 引用计数为 1
        
    NSLog(@"%@", stringCopy);                       // 内容与 string 相同
    NSLog(@"%@", mstringMCopy);                     // 内容与 string 相同
        
    [mstringMCopy appendString:@"!!"];              // mstringMCopy 是可变的
        
    NSLog(@"%@", mstringMCopy);
    
    /*
            string 和 stringCopy 指向的是同一块内存区域(又叫 apple 弱引用 weak reference),此时 stringCopy 的
        引用计数和 string 的一样都为2。而 mstringMCopy 则是我们所说的真正意义上的复制,系统为其分配了新内存,但指针
        所指向的字符串还是和 string 所指的一样。
    */

    NSMutableString *mstring = [NSMutableString stringWithString: @"origion"];
        
    NSString *stringCopy = [mstring copy];
    NSMutableString *mStringCopy = [mstring copy];
        
    NSMutableString *mstringMCopy = [mstring mutableCopy];
        
    NSLog(@"%p", mstring);
    NSLog(@"%p", stringCopy);                       // 地址与 string 不同
    NSLog(@"%p", mStringCopy);                      // 地址与 string 不同,与 stringCopy 相同
    NSLog(@"%p", mstringMCopy);                     // 地址与 string 不同
        
    NSLog(@"%@", mstring);
    NSLog(@"%@", stringCopy);                       // 内容与 string 相同
    NSLog(@"%@", mStringCopy);                      // 内容与 string 相同
    NSLog(@"%@", mstringMCopy);                     // 内容与 string 相同
        
    [mstring appendString:@" origion!"];
        
    // [mStringCopy appendString:@"mm"];            // error,mStringCopy 不可变
        
    [mstringMCopy appendString:@"!!"];              // mstringMCopy 可变
        
    NSLog(@"%@", mstring);
    NSLog(@"%@", mstringMCopy);

    /* 
        以上四个 NSString 对象所分配的内存都是不一样的。但是对于 mStringCopy 其实是个不可变对象,所以上述会报错。
    */

2、系统的容器类对象

    /*
        系统的容器类对象指 NSArray,NSDictionary 等。
         
        对于容器类本身,上面讨论的结论也是适用的,需要探讨的是复制后容器内对象的变化。
         
            对于系统的容器类对象,copy 返回不可变对象,是指针复制,包括里面的元素也是指向相同的指针。mutablecopy 
        返回可变对象,是对象复制,其中容器内的元素内容都是指针复制,可以改变其内的元素。
    */

    NSArray *array = [NSArray arrayWithObjects:@"a", @"b", @"c", nil];
        
    NSArray *arrayCopy = [array copy];
        
    NSMutableArray *mArrayMCopy = [array mutableCopy];
        
    NSLog(@"%p", array);
    NSLog(@"%p", arrayCopy);                        // 地址与 array 相同
    NSLog(@"%p", mArrayMCopy);                      // 地址与 array 不同
        
    NSLog(@"%zi",[array retainCount]);
    NSLog(@"%zi",[arrayCopy retainCount]);
    NSLog(@"%zi",[mArrayMCopy retainCount]);
        
    NSLog(@"%@", array);
    NSLog(@"%@", arrayCopy);                        // 内容与 array 相同
    NSLog(@"%@", mArrayMCopy);                      // 内容与 array 相同
        
    [mArrayMCopy addObject:@"de"];                  // mArrayMCopy 可变
        
    NSLog(@"%@", array);
    NSLog(@"%@", mArrayMCopy);
        
    [mArrayMCopy removeObjectAtIndex:0];
        
    NSLog(@"%@", array);
    NSLog(@"%@", mArrayMCopy);
    
    /*
            copy 返回不可变对象,mutablecopy 返回可变对象,arrayCopy 是指针复制,而 mArrayMCopy 是对象复制。
        arrayCopy 是和 array 同一个 NSArray 对象(指向相同的对象),包括 array 里面的元素也是指向相同的指针。
        mArrayMCopy 是 array 的可变副本,指向的对象和 array 不同,但是其中的元素和 array 中的元素指向的是同一
        个对象。mArrayMCopy 还可以改变其内的元素:删除或添加。但是注意的是,容器内的元素内容都是指针复制。
    */
    
    NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"a"], @"b", @"c", nil];
        
    NSArray *arrayCopy = [array copy];
        
    NSMutableArray *mArrayMCopy = [array mutableCopy];
        
    NSLog(@"%p", array);
    NSLog(@"%p", arrayCopy);                        // 地址与 array 相同
    NSLog(@"%p", mArrayMCopy);                      // 地址与 array 不同
        
    NSLog(@"%zi",[array retainCount]);
    NSLog(@"%zi",[arrayCopy retainCount]);
    NSLog(@"%zi",[mArrayMCopy retainCount]);
        
    NSLog(@"%@", array);
    NSLog(@"%@", arrayCopy);                        // 内容与 array 相同
    NSLog(@"%@", mArrayMCopy);                      // 内容与 array 相同
        
    NSMutableString *testString = [array objectAtIndex:0];
    [testString setString:@"1a1"];                  // 这样会改变 testString 的指针,其实是将 @“1a1” 临时对象
                                                       赋给了 testString。这样以上三个数组的首元素都被改变了。
        
    NSLog(@"%@", array);
    NSLog(@"%@", arrayCopy);                        // 内容与 array 相同
    NSLog(@"%@", mArrayMCopy);                      // 内容与 array 相同
    
    /*
            arrayCopy,mArrayMCopy 和 array 指向的都是不一样的对象,但是其中的元素都是一样的对象,同一个指针。
        对于容器而言,其元素对象始终是指针复制。如果需要元素对象也是对象复制,就需要实现深拷贝。
    */
    
    NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"first"], @"b", @"c", nil];
        
    NSArray *deepCopyArray = [[NSArray alloc] initWithArray:array copyItems: YES];
    
    // 使用归档/反归档拷贝
    NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject: array]];   
        
    NSLog(@"%@", array);
    NSLog(@"%@", deepCopyArray);                    // 内容与 array 相同
    NSLog(@"%@", trueDeepCopyArray);                // 内容与 array 相同
        
    NSMutableString *testString1 = [array objectAtIndex:0];
    [testString1 setString:@"1a1"];
        
    NSLog(@"%@", array);
    NSLog(@"%@", deepCopyArray);                    // 内容与 array 不同
    NSLog(@"%@", trueDeepCopyArray);                // 内容与 array 不同
    
    /*
            trueDeepCopyArray 是完全意义上的深拷贝,而 deepCopyArray 则不是,对于 deepCopyArray 内的不可变元素其还是
        指针复制。或者我们自己实现深拷贝的方法。因为如果容器的某一元素是不可变的,那你复制完后该对象仍旧是不能改变的,因此只需要
        指针复制即可。除非你对容器内的元素重新赋值,否则指针复制即已足够。举个例子,[[array objectAtIndex:0] appendstring:@”sd”] 
        后其他的容器内对象并不会受影响。[[array objectAtIndex:1] 和 [[deepCopyArray objectAtIndex:1] 尽管是指向同一块内存,
        但是我们没有办法对其进行修改——因为它是不可改变的。所以指针复制已经足够。所以这并不是完全意义上的深拷贝,但是 apple 的官方
        文档将其列为 deep copy 了,并添加了 copy 和 mutablity 的关系说明。
    */

3、自定义对象

  • 需要自己要实现 NSCopying,NSMutableCopying 这样就能调用 copy 和 mutablecopy 了。
    // Teacher.h

        // 遵守 NSCopying, NSMutableCopying 协议
        @interface Teacher : NSObject <NSCopying, NSMutableCopying>
        
        @property (nonatomic, copy) NSString *name;
        @property (nonatomic, assign) int age;
        
        @end

    // Teacher.m

        @implementation Teacher
        
        // 实现协议方法,委托方会自动调用协议中的该方法
        - (id)copyWithZone:(NSZone *)zone{
            
            Teacher *copy = [[Teacher allocWithZone:zone] init];
                
            copy.name = self.name;
            copy.age = self.age;
               
            return copy;
        }
        
        // 实现协议方法,委托方会自动调用协议中的该方法
        - (id)mutableCopyWithZone:(NSZone *)zone{
            
            Teacher *copy = [[Teacher allocWithZone:zone] init];          
            
            copy.name = self.name;
            copy.age = self.age;
            
            return copy;
        }
        
        - (NSString *)description{
            
            return [NSString stringWithFormat:@"%@  %i", self.name, self.age];
        }
        
        @end

    // main.m

        #import "Teacher.h"
        
        Teacher *tch = [[Teacher alloc] init];
        tch.name = @"xiao xin";
        tch.age = 28;
            
        // 内容复制
        Teacher *tchCpy = [tch copy];
        
        // 内容复制
        Teacher *tchMCopy = [tch mutableCopy];
        
        tchCpy.age = 20;
        tchMCopy.age = 22;
        
        NSLog(@"%p", tch);
            
        // tchCpy 与 tch 的地址不同
        NSLog(@"%p", tchCpy);
        
        // tchMCopy 与 tch 的地址不同
        NSLog(@"%p", tchMCopy);
        
        NSLog(@"%@", tch);
            
        // tchCpy 与 tch 的实例变量的值不同
        NSLog(@"%@", tchCpy);
        
        // tchMCopy 与 tch 的实例变量的值不同
        NSLog(@"%@", tchMCopy);

    // Children.h

        // 遵守 NSCopying, NSMutableCopying 协议
        @interface Children : NSObject <NSCopying, NSMutableCopying>
        
        @property (nonatomic, copy) NSString *name;
        @property (nonatomic, assign) int age;
        
        @end

    // Children.m

        @implementation Children
        
        // 实现协议方法,委托方会自动调用协议中的该方法。
        - (id)copyWithZone:(NSZone *)zone{
            
            // 若有类继承此类,并且此方法在子类中可用
            id copy = [[[self class] allocWithZone:zone] init];
                
            [copy setName:self.name];
                
            [copy setAge:self.age];
                
            return copy;
        }
        
        // 实现协议方法,委托方会自动调用协议中的该方法。
        - (id)mutableCopyWithZone:(NSZone *)zone{
            
            // 若有类继承此类,并且此方法在子类中可用
            id copy = [[[self class] allocWithZone:zone] init];
                
            [copy setName:self.name];
                
            [copy setAge:self.age];
                
            return copy;
        }
        
        @end

    // GoodChildren.h

        @interface GoodChildren : Children
        
        @property (nonatomic, assign)int score;
        
        @end

    // GoodChildren.m

        @implementation GoodChildren
        
        // 实现协议方法,委托方会自动调用协议中的该方法。
        - (id)copyWithZone:(NSZone *)zone{                                      
            
            GoodChildren *copy = [super copyWithZone:zone];
                
            copy.score = self.score;
                
            return copy;
        }
        
        // 实现协议方法,委托方会自动调用协议中的该方法。
        - (id)mutableCopyWithZone:(NSZone *)zone{                               
                
            GoodChildren *copy = [super copyWithZone:zone];
                
            copy.score = self.score;
                
            return copy;
        }
        
        - (NSString *)description{
            
            return [NSString stringWithFormat:@"%@  %i  %d", self.name, self.age, self.score];
        }
        
        @end

    // main.m

        #import "GoodChildren.h"
        
        GoodChildren *gChild = [[GoodChildren alloc] init];
        gChild.name = @"xiao xin";
        gChild.age = 18;
        gChild.score = 88;
            
        // 内容复制
        GoodChildren *gChildCopy = [gChild copy];
        
        // 内容复制
        GoodChildren *gChildMCopy = [gChild mutableCopy];
            
        gChildCopy.age = 20;
        gChildMCopy.age = 22;
            
        gChildCopy.score = 100;
        gChildMCopy.score = 120;
            
        NSLog(@"%p", gChild);
            
        // gChildCopy 与 gChild 的地址不同
        NSLog(@"%p", gChildCopy);
        
        // gChildMCopy 与 gChild 的地址不同
        NSLog(@"%p", gChildMCopy);
            
        NSLog(@"%@", gChild);
            
        // gChildCopy 与 gChild 的实例变量的值不同
        NSLog(@"%@", gChildCopy);
        
        // gChildMCopy 与 gChild 的实例变量的值不同
        NSLog(@"%@", gChildMCopy);
目录
相关文章
|
安全 数据安全/隐私保护 iOS开发
iOS 初探代码混淆(OC)
自己做iOS开发也有几年的时间了,平时做完项目基本就直接打包上传到Appstore上,然后做上架操作了。但是最近,客户方面提出了代码安全的要求。说是要做代码混淆,这方面的工作之前从来没有接触过。然后就上网查了一下,原来有很多应用程序都做了代码混淆。看来是我固步自封了......
iOS 初探代码混淆(OC)
|
Swift iOS开发
iOS OC混编Swift 后者无法走断点
iOS OC混编Swift 后者无法走断点
244 0
|
iOS开发
iOS block修饰符用copy还是strong
iOS block修饰符用copy还是strong
395 0
|
开发工具 git iOS开发
iOS开发 - 不小心删除了Copy Bundle Resources怎么办?(多人开发频繁添加删除图片导致图片路径重复,编译报错)
iOS开发 - 不小心删除了Copy Bundle Resources怎么办?(多人开发频繁添加删除图片导致图片路径重复,编译报错)
493 0
iOS开发 - 不小心删除了Copy Bundle Resources怎么办?(多人开发频繁添加删除图片导致图片路径重复,编译报错)
|
存储 Unix 编译器
|
存储 算法 iOS开发
|
Swift iOS开发 容器
iOS 仿支付宝银行卡界面(支持Swift/OC)
在有支付相关的APP中,都有对应的钱包,虽然现在的支付宝,微信支付很流行,但是都是需要绑定自己的银行卡,那么这个银行卡的卡包页面该怎么实现呢?在网上找了许久也没有找到合适的,那就索性自己造轮子。
653 0
|
编解码 Swift iOS开发
iOS 应用之间的跳转(OC&Swift)
iOS 应用之间的跳转(OC&Swift)
2201 0
iOS 应用之间的跳转(OC&Swift)
|
开发工具 Swift iOS开发
iOS 问题 1: OC项目导入swift三方库不兼容问题
iOS 问题 1: OC项目导入swift三方库不兼容问题
1848 0
iOS 问题 1: OC项目导入swift三方库不兼容问题
|
iOS开发
iOS逆向 04:OC反汇编(下)
iOS逆向 04:OC反汇编(下)
495 0
iOS逆向 04:OC反汇编(下)