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?

C和OC的基础语法(易混淆的概念知识)

关于iOS的深copy和浅copy 以及copy 和 strong 的区别

iOS面试系列-1iOS中@property 后assign,copy,retain,weak,strong的意义与区别(必考-必须详细掌握)

iOS 属性之 strong和copy修饰的区别