关于OC中Copy和retain的心得

来源:互联网 发布:电视盒子 软件应用破解 编辑:程序博客网 时间:2024/05/16 18:09

关于oc中的 @property中的retainCopy的运用,虽然描述可能有些冗长但相信坚持看完你一定能有所收获

首先先看一下OC中的关于* &之间的关系

        NSString * str=@"123";

        NSLog(@"%@",str);

        NSLog(@"%@",*&str);

        NSLog(@"%p",str);

        NSLog(@"%p",&str);

2015-09-15 13:07:31.772 test123[1359:52116] 123

2015-09-15 13:07:31.773 test123[1359:52116] 123

2015-09-15 13:07:31.773 test123[1359:52116] 0x100001038

2015-09-15 13:07:31.773 test123[1359:52116] 0x7fff5fbff728

1)*取得是指针指向的内容,&取得是内容指向的地址 所以*&str和str的内容是相同的 
  2)而用%p格式打印的str指的是将str的内容以地址的形式打印出来,也就是@"123"的地址,之所以用%p格式打      印str打印的是地址因为 在str内本身存放的就是地址
  3)而%p,&str则打印的是 str的地址
关于copy

 copy分为浅拷贝和深copy 

 copy方式又分为copymutableCopy两种

要实现Copy或者mutableCopy就要遵守

<NSCopying>或者<NSMutableCopying>协议并且实现 

-(id)mutableCopyWithZone:(NSZone *)zone

-(id)copyWithZone:(NSZone *)zone

不然是不能实现Copy的

系统给出的已经实现了的有

NSString ,NSArray,NSData;NSDictionary

至于NSNumber因为其本身不可变所以可以执行Copy但不能mutableCopy

其它的类要想使用Copy都要遵循协议并实现方法

 -(id)copyWithZone:(NSZone *)zone

     {

      Dog *dog=[[[[Dog class]allocWithZone:zone]init]autoRelease];

      dog.name=self.name;

      dog.age=self.age;

      return dog;

   }


关于两种Copy的区别

    NSString *str=@"123";

   NSString *s1=[strcopy];

   NSString * s2=[strmutableCopy];

   NSMutableString * s3=[strcopy];

   NSMutableString * s4=[strmutableCopy];

   NSMutableString * s5=[s4copy];

   NSMutableString * s6=[s4mutableCopy];

   NSString * s7 =[s4copy];

   NSString * s8=[s4mutableCopy];

   NSLog(@"str%@ ,%p, %p",str,str,&str);

    NSLog(@"s1 %@ ,%p, %p",s1,s1,&s1);

    NSLog(@"s2 %@ ,%p, %p",s2,s2,&s2);

    NSLog(@"s3 %@ ,%p, %p",s3,s3,&s3);

    NSLog(@"s4 %@ ,%p, %p",s4,s4,&s4);

    NSLog(@"s5 %@ ,%p, %p",s5,s5,&s5);

    NSLog(@"s6 %@ ,%p, %p",s6,s6,&s6);

    NSLog(@"s7 %@ ,%p, %p",s7,s7,&s7);

    NSLog(@"s8 %@ ,%p, %p",s8,s8,&s8);

2015-09-15 13:33:17.736 test111[1434:61053] str 123 ,0x1058c07f0,    0x7fff5a344c30

2015-09-15 13:33:17.737 test111[1434:61053] s1 123 ,0x1058c07f0,    0x7fff5a344c28

2015-09-15 13:33:17.737 test111[1434:61053] s2 123 ,0x7fe9405435a0, 0x7fff5a344c20

2015-09-15 13:33:17.737 test111[1434:61053] s3 123 ,0x1058c07f0,    0x7fff5a344c18

2015-09-15 13:33:17.738 test111[1434:61053] s4 123 ,0x7fe940537c00, 0x7fff5a344c10

2015-09-15 13:33:17.738 test111[1434:61053] s5 123 ,0x7fe940542820, 0x7fff5a344c08

2015-09-15 13:33:17.738 test111[1434:61053] s6 123 ,0x7fe9405451a0, 0x7fff5a344c00

2015-09-15 13:33:17.738 test111[1434:61053] s7 123 ,0x7fe940537c40, 0x7fff5a344bf8

2015-09-15 13:33:17.738 test111[1434:61053] s8 123 ,0x7fe940545ab0, 0x7fff5a344bf0

根据结果可以看到是S1-S8所有用%@ 格式打印出来的都是123 而 str ,s1 ,  s3用%p打印出来得是相同的 而所有的用 &str得到的都是不同的因为 s1-s8它们本身是不同的
所以有结论

   1. 凡是mutableCopy的都是内容

   2. 源对象是可变的话Copy/mutableCopy的都是内容;

   3. 源对象是不变的话Copy的都是地址也就是指针

   4. 例如s3 如果源对象是不可变的那么将源对象copy后是可以用一个可变的字符串进行接收的,如果不改变接收对象的值的话是没有问题的但是如果将接收对象进行增删改的话就会程序就会崩溃 

 reason: 'Attempt to mutate immutable object with appendFormat 

因为你尝试将一个不可变的对象进行可变操作


关于retain和Copy的区别

@property(retain)NSString *rStr;

@property(copy)NSString *cStr;

   

  //源对象是不可变的

    NSString *str=@"123";

    self.rStr=str;

    self.cStr=str;

    NSLog(@"str:%p,%p",str,&str);

    NSLog(@"rStr:%p %p",self.rStr,&_rStr);

    NSLog(@"cStr:%p %p",self.cStr,&_cStr);

    

2015-09-15 13:45:00.083 test111[1467:64776] str: 0x105b767f0,0x7fff5a08ec38

2015-09-15 13:45:00.083 test111[1467:64776] rStr:0x105b767f0 0x7fdbc0455138

2015-09-15 13:45:00.084 test111[1467:64776] cStr:0x105b767f0 0x7fdbc0455140

   retain只是将源对象 的计数器加一 所以rStr的str%p的结果是相同的因为它们指向的是相同的对象

可以看到 因为源对象是不可变的所以只需要copy其地址就可以了不需要重新开辟地址占用资源 所以Copy的都是地址也就是通常所谓的指针拷贝

到这里再看一个例子

 NSString * s=@"test";

 NSLog(@"%ld",s.retainCount);

 [s retain];

 NSLog(@"%ld",s.retainCount);

 [s release];

 NSLog(@"%ld",s.retainCount);

2015-09-16 19:11:12.234 test[4544:2056420] -1

2015-09-16 19:11:12.235 test[4544:2056420] -1

2015-09-16 19:11:12.235 test[4544:2056420] -1

因为@"test"是以个OC的字符串常量,不管对test执行多少次retain和release系统堆不会报错其引用计数为-1 或者是一个非常大的值 系统是不会去释放一个常量的;

NSString *str0=[NSStringstringWithFormat:@"test%d",2];

NSLog(@"%ld",str0.retainCount);

NSString *str = [NSStringstringWithString:[NSStringstringWithFormat:@"test%d",2]];

NSLog(@"%ld",str.retainCount);

NSString * str3= 

NSLog(@"%ld",str3.retainCount);

2015-09-16 19:23:31.418 test[4627:2061789] 1

2015-09-16 19:23:31.418 test[4627:2061789] 2

2015-09-16 19:23:31.418 test[4627:2061789] 3

可以看到 通过[NSString stringWithFormat:@"test%d",2]; [NSString stringWithString:[NSString stringWithFormat:@"test%d",2]]; [NSString stringWithString:str];

但是当使用[NSString stringWithString:@"test"]这样方式创建str的时候系统会警告 Using 'stringWithString' with a literal  is redundant 告诉我们使用不必要的创建方式


 NSString * str4=str3;

    NSLog(@"%ld",str4.retainCount);

2015-09-16 19:23:31.418 test[4627:2061789] 3

可以看到用=赋值语句不会增加引用计数只是简单的赋值 assign


NSString * str5=[str4retain];

 NSLog(@"%ld",str5.retainCount);

2015-09-16 19:23:31.419 test[4627:2061789] 4

NSString * str6=[str5copy];

NSLog(@"%ld",str6.retainCount);

2015-09-16 19:23:31.419 test[4627:2061789] 5

结合上面的retain和Copy的区别共同点可以看到 retain和Copy都会将引用计数加一

以上可以直接将引用计数加一的原因就是源对象和目标对象都是不可变的 所以这时候只需要将内容的引用计数加一就好不需要从新开辟内存 节省内存资源

 NSMutableString * str7=[str6mutableCopy];

 NSLog(@"%ld",str7.retainCount);

2015-09-16 19:23:31.419 test[4627:2061789] 1

而现在mutableCopy的结果是一个可变的 如果还用简单的将引用计数加一的话 当str7改变时 str6也会跟着改变这样前面所用引用都会改变 这是简单的将retain计数加一是不能满足需求的 ,就需要内存中重新开辟一片空间并且将str6中的内容全部Copy过去这时候 str7中存放的就是地址就和str6中存放的地址不一样了 这样堆str7进行操作根str6再无关联

看完str在看Array

 NSArray * arr=@[@"1",@"2"];

    NSLog(@"%ld",arr.retainCount);

   NSArray * arr1=[arr copy];

    NSLog(@"%ld",arr1.retainCount);

    NSLog(@"%ld",arr.retainCount);

   NSMutableArray * arr2=[arr1 copy];

    NSLog(@"%ld",arr2.retainCount);

   NSMutableArray * arr3=[arr2 mutableCopy];

   NSLog(@"%ld",arr3.retainCount);

2015-09-16 19:23:31.419 test[4627:2061789] 1

2015-09-16 19:23:31.419 test[4627:2061789] 2

2015-09-16 19:23:31.419 test[4627:2061789] 2

2015-09-16 19:23:31.419 test[4627:2061789] 3

2015-09-16 19:23:31.419 test[4627:2061789] 1

结果和str也是相同的





   //源对象是可变的

    NSMutableString *mStr=[NSMutableStringstringWithFormat:@"abc"];

    self.rStr=mStr;

    self.cStr=mStr;

    NSLog(@"mStr:%p,%p",mStr,&mStr);

    NSLog(@"rStr:%p %p",self.rStr,&_rStr);

    NSLog(@"cStr:%p %p",self.cStr,&_cStr);

2015-09-15 13:45:00.084 test111[1467:64776] mStr:0x7fdbc0524410,0x7fff5a08ec30

2015-09-15 13:45:00.084 test111[1467:64776] rStr:0x7fdbc0524410 0x7fdbc0455138

2015-09-15 13:45:00.084 test111[1467:64776] cStr:0x7fdbc0524b10 0x7fdbc0455

   对于可变的 因为 retain只是将计数加1所以 rStr 和 mStr用%p打印的结果还是相同的,因为它们指向的仍然是相同的对象

而 Copy的结果是不同的因为 源对象是可变的Copy完的结果也会进行增删改所以如果Copy指针的话那么源对象改变的时候或者目标对象改变的时候两者都会改变,当源对象/目标对象被释放的时候,目标对象或者源对象也会被释放的。而我们一般都不希望 在Copy完了以后两者还联系 一个被释放了另一个也被释放,所以会从新开辟一段新地址

其中的内容是和源内容是相同的。



@property(retain)NSString *name;

-(void)setName:(NSString *)name{

   if (self.name !=name) {

        [self.namerelease];

        [[self.name = name]retain];

    }

 }

@property(copy)NSString *name;

-(void)setName:(NSString *)name{

   if (self.name !=name) {

        [self.namerelease];

        [[self.name = name]copy];

    }


}

所以将属性self.name=nil
这是一个set方法所以会调用set方法因为原来的那么不为空 所以会将原来的地址释放掉 并且将self.name=nil,在OC中对nil进行retain和Copy是合法的所以对属性在delloc中的释放只需要将属性赋值为nil就可以了

当然以上所有的都是针对一级引用即源对象之中的内容本身不是直接存放的对象地址 如果源对象之中的内容存放的本来就是指针 那么无论是Copy还是mutableCopy的结果都是Copy的地址只是深Copy的是内层容器中存放的地址而浅拷贝Copy的是外层容器中存放的地址
















0 0