OC基础 - strong和copy的区别【转载】
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OC基础 - strong和copy的区别【转载】相关的知识,希望对你有一定的参考价值。
参考技术A 在ios开发中,几乎每天都会遇到 NSString 属性的声明,
在 ARC内存管理机制 下,
NSString属性声明有两个关键字可以选择: strong 和 copy ;
那么问题来了,什么时候用 strong ,什么时候用 copy ?
下面我写一个小demo,希望大家能看懂,也还请路过的大神指教!
我在.h文件中声明了两个 NSString 属性,如下:
结论:这种情况下,不管是用strong还是copy修饰的对象,其指向的地址都是originStr的地址。
然后我们运行一下,打印结果如下图:
咱们先不解释,卖个关子,我们接着往下看。
OK,这回我们终于看到我们希望看到的结果了,
_copyyStr依然是“hello,everyone”,没有变成“hello,QiShare”,
_copyyStr指针指向的地址不再是_originStr的地址。
细心的同学会发现,第三种在赋值的时候用了点语法,而不是直接赋值。
除了将 _strongStr = originStr2; 改为 self.strongStr = originStr3;
_copyyStr = originStr2;改为 self.copyyStr = originStr3;
其余完全一样。
也就是说,我们将_copyyStr = originStr2;改为 self.copyyStr = originStr3;才导致了_copyyStr的值在第三种情况下依然没有改变,这是为什么呢?
当我们用@property来声明属性变量时,编译器会自动为我们生成一个以下划线加属性名命名的实例变量(@synthesize copyyStr = _copyyStr),并且生成其对应的getter、setter方法。
当我们用self.copyyStr = originStr赋值时,会调用coppyStr的setter方法,而_copyyStr = originStr 赋值时给_copyyStr实例变量直接赋值,并不会调用copyyStr的setter方法,而在setter方法中有一个非常关键的语句:
结论:第三种场景中用self.copyyStr = originStr 赋值时,调用copyyStr的setter方法,setter方法对传入的copyyStr做了次深拷贝生成了一个新的对象赋值给_copyyStr,所以_copyyStr指向的地址和对象值都不再和originStr相同。
这里我们将_copyyStr = originStr;改成了self.copyyStr = originStr;
原因:这里的copy是浅拷贝,并没有生成新的对象
由上面的例子可以得出:
但是,我们一般声明 NSString 时,也不希望它改变,所以一般情况下,建议使用 copy ,这样可以避免 NSMutableStrin g带来的错误。
我们都知道, assign 用来修饰基本数据类型, weak 用来修饰OC对象。
其实照理, assign 也能修饰OC对象,但是 assign 修饰的对象在该对象释放后,其指针依然存在,不会被置为nil——这就造成了一个很严重的问题:出现了野指针。当访问这个野指针时,指向了原地址,而原地址有两种情况:
但是用 weak 来修饰的话,对象释放的时候会把指针置为 nil ,从而避免了 野指针 的出现。
那又有个疑问出现了,凭什么基本数据类型就可以使用 assign 。这就要扯到堆和栈的问题了,基本数据类型会被分配到栈空间,而栈空间是由 系统自动管理分配和释放 的,就不会造成野指针的问题。
iOS的属性声明:retain和strong的区别
声明属性时用strong或者retain效果是一样的(貌似更多开发者更倾向于用strong)。不过在声明Block时,使用strong和retain会有截然不同的效果。strong会等于copy,而retain竟然等于assign!
当然定义Block还是应该用copy(还有其他需要注意的地方,可以参考这篇文章:iOS: ARC和非ARC下使用Block属性的问题),因为非ARC下不copy的Block会在栈中,ARC中的Block都会在堆上的。
可以这样复现问题。在非ARC环境下,定义一个简单类型,定义一个Block属性,先用正确的copy:
@interface TestCls : NSObject @property (nonatomic, copy) void(^myBlock)(); @end
在另一个类型里声明变量:
TestCls *_testObj
然后在一个方法里,比如viewDidLoad中,设置Block变量,注意即便是在非ARC下,没有引用外部变量的Block类型也是NSGlobalBlock,而引用外部变量的Block才是NSStackBlock,如下代码
_testObj = [[TestCls alloc] init]; int outerVar = 12; _testObj.myBlock = ^void() { NSLog(@"Block被调用:%d", outerVar); }; NSLog(@"Block类型:%@", [_testObj.myBlock class]);
然后在另一个方法里(比如UIButton的点击事件方法里)去执行Block,如下:
_testObj.myBlock();
测试环境是Xcode 6/iOS 7/8,如果最上面myBlock属性声明是copy或者strong的话,Block都会被copy,输出:
Block类型:__NSMallocBlock__ Block被调用:12
如果上面myBlock属性声明是retain或者assign的话,Block表现起来就是assign,没有去copy,输出
Block类型:__NSStackBlock__
此时可能会Crash(BAD_ACCESS),也有可能输出错误的值(我在控制台下测试会出现这种情况)。因为Block作用域在函数栈里,而函数已经执行完毕了。
总之,strong和retain竟然有不一样的地方,而声明Block属性请务必用copy。
Related posts:
iOS: 非ARC下返回Block iOS: ARC和非ARC下使用Block属性的问题 iOS: NSData/NSMutableData的二进制数据读写 在线工具: 从Objective-C .m文件中提取函数定义
以上是关于OC基础 - strong和copy的区别【转载】的主要内容,如果未能解决你的问题,请参考以下文章
iOS OC中strong,weak,assign,copy等的区别
Objc中为何某些类的属性要设置为copy而不是strong?
关于iOS的深copy和浅copy 以及copy 和 strong 的区别
iOS面试系列-1iOS中@property 后assign,copy,retain,weak,strong的意义与区别(必考-必须详细掌握)