举个栗子:
@property(nonatomic , assign)void *p;
@property(nonatomic , copy)void (^block)(void);
为啥指针要用assign, 为啥block要定义成copy?
聊聊属性
- 对于Objective-C中的一般object,最好使用用retain。一些特别的object(例如:string)使用copy。
而assign关键字代表setter直接赋值,而不是复制或者保留它,更不需要进行引用计数。这种机制非常适合一些基本数据类型,比如BOOL, NSInteger和CGFloat,或者你并不直接拥有的类型,比如delegate。
只有基本数据类型是gc(gc就是垃圾回收的意思)对象,其它都是非gc的。比如int,float等基本数据类型。而NSString是非gc类型,所以用assign就不适合,而推荐做法是NSString用copy。而类如NSArray,NSDate等其它objc类型,推荐用 retain。
而copy与retain(ARC对应是strong)的具体区别为:copy其实是建立了一个相同的对象,而retain只是保存其对象,并且其计数值+1。
例如:一个NSString对象,地址为0×1000,内容为@”string” copy到另外一个NSString之后,地址为0×2000,内容相同,新的对象retain为1,旧有对象没有变化 retain到另外一个NSString之后,地址相同(建立一个指针,指针拷贝),内 容当然相同,但是这个新对象的retain值+1,并释放旧的对象也就是说,retain是 指针拷贝,copy是内容拷贝。
- atomicity的默认值是atomic,读取函数为原子操作。
atomic是保证读取变量是线程安全的,即它会保证每次getter和setter的操作都会正确的执行完毕,而不用担心其它线程在你get的时候set,可以说保证了某种程度上的线程安全。nonatomic是不能保证线程安全的。但是nonatomic比atomic速度要快。
这也是为什么property基本上都用nonatomic了。
- @property有一对属性:strong 和 weak。形象的解释:把对象想象成一条狗,它要跑 (be deallocated)。
强指针就像一条拴在狗脖子上的狗链;只要攥在手里,狗就跑不了;如果5个人攥着5条狗链都拴着狗 (5个强指针指向对象),除非5条狗链都撒开,狗就跑不了。弱指针就像是孩子指着狗喊“看!狗!”;只要狗链还拴着狗,孩子就能指着狗喊。当所有狗链都撒开,不管有多少孩子指着狗喊,狗都跑了。当最后一个强指针不再指向对象,对象就会被释放,所有弱指针清零。我们什么时候使用弱指针呢?只有当你想避免保留循环 (retain cycles,) 时,我们才使用它。
什么时候用什么定义
assign :
简单赋值,不更改索引计数
假设你用malloc分配了一块内存,并且把它的地址赋值给了指针a,后来你希望指针b也共享这块内存,于是你又把a赋值给(assign)了b。此时a 和b指向同一块内存,请问当a不再需要这块内存,能否直接释放它?答案是否定的,因为a并不知道b是否还在使用这块内存,如果a释放了,那么b在使用这块内存的时候会引起程序crash掉,
*/应用场合:
a. 对基础数据类型 (例如NSInteger,CGFloat)和C数据类型(int, float, double, char, 等)
b. 适用简单数据类型
c. delegateretain:
与strong相对应,使用了引用计数,retain+1,release -1;当引用 计数为0时,dealloc会被调用,内存被释放copy:
用于非共享内存时,每个指针有自己的内存空间
应用场合:NSStringatomic//默认属性
A,当一个变量声明为atomic时,意味着在多线程中只能有一个线程能对它进行访问B,当一个变量声明为atomic时,该变量为线程安全型,但是会影响访问速度,C,当一个变量声明为atomic时,在非ARC编译环境下,需要设置访问锁来保证对该变量进行正确的get/setnonatomic
A, 当一个变量声明为nonatomic时,意味着多个线程可以同时对其进行访问
B, 当一个变量声明为nonatomic时,它是非线程安全型,访问速度快;
C, 当一个变量声明为nonatomic时,当两个不同的线程对其访问时,容易失控。
总结:atomic和nonatomic用来决定编译器生成的getter和setter是否为原子操作。在多线程环境下,原子操作是必要的,否则有可能引起错误的结果strong: //ARC中默认属性,等于非ARC中的retain
与retain相对应.
应用场景:
strong属性用于ARC中
@property (nonatomic, strong) ViewController *viewController;
NSArray,NSDate等其它objc
- weak:
与assign 相对应,
如果这样声明两个属性:
@property (nonatomic, strong) NSString *string1;
@property (nonatomic, weak) NSString *string2;
并定义
@synthesize string1;
@synthesize string2;
再来猜一下,下面输出是什么?
self.string1 = @"String 1";
self.string2 = self.string1;
self.string1 = nil;
NSLog(@"String 2 = %@", self.string2);
结果是:String 2 = null
分析一下,由于self.string1与self.string2指向同一地址,且string2没有retain内存地址,而 self.string1=nil释放了内存,所以string1为nil。声明为weak的指针,指针指向的地址一旦被释放,这些指针都将被赋值为 nil。这样的好处能有效的防止野指针。在c/c++开发过程中,为何大牛都说指针的空间释放了后,都要将指针赋为NULL. 在这儿用weak关键字帮我们做了这一步。
应用场景:
用于IBOutlets,如,UIViewController的子类,即一般的控件。
@property (nonatomic, weak) IBOutlet UIButton *myButton;
还有一种情况你需要使用weak(默认是strong),就是为了避免retain cycles(就是父类中含有子类{父类retain了子类,这个在之前的系列里有介绍(iOS简单学之8-变量的属性)},子类中又调用了父类{子类又retain了父类},这样都无法release。
提问:
大部分的时候NSString的属性都是copy,那copy与strong的情况下到底有什么区别呢?
比如:
@property (retain,nonatomic) NSString *rStr;
@property (copy, nonatomic) NSString *cStr;
- (void)test:
{
NSMutableString *mStr = [NSMutableStringstringWithFormat:@"abc"];
self.rStr = mStr;
self.cStr = mStr;
NSLog(@"mStr:%p,%p", mStr,&mStr);
NSLog(@"retainStr:%p,%p", _rStr, &_rStr);
NSLog(@"copyStr:%p,%p", _cStr, &_cStr);
}
假如,mStr对象的地址为0x11,也就是0x11是@“abc”的首地址,mStr变量自身在内存中的地址为0x123;
当把mStr赋值给retain的rStr时,rStr对象的地址为0x11,rStr变量自身在内存中的地址为0x124;rStr与mStr指向同样的地址,他们指向的是同一个对象@“abc”,这个对象的地址为0x11,所以他们的值是一样的。
当把mStr赋值给copy的cStr时,cStr对象的地址为0x22,cStr变量自身在内存中的地址0x125;cStr与mStr指向的地址是不一样的,他们指向的是不同的对象,所以copy是深复制,一个新的对象,这个对象的地址为0x22,值为@“abc”。
如果现在改变mStr的值:
[mStr appendString:@"de"];
NSLog(@"retainStr:%@", _rStr);
NSLog(@"copyStr:%@", _cStr);
结果,
使用retain的字串rStr的值:@"abcde",
而使用copy的字串cStr的值:@"abc",
所以,如果一般情况下,我们都不希望字串的值跟着mStr变化,所以我们一般用copy来设置string的属性。
如果希望字串的值跟着赋值的字串的值变化,可以使用strong,retain。
注意:上面的情况是针对于当把NSMutableString赋值给NSString的时候,才会有不同,如果是赋值是NSString对象,那么使用copy还是strong,结果都是一样的,因为NSString对象根本就不能改变自身的值,他是不可变的。
把一个对象赋值给一个属性变量,当这个对象变化了,如果希望属性变量变化就使用strong属性,如果希望属性变量不跟着变化,就是用copy属性。
由此可以看出:
对源头是NSMutableString的字符串,retain仅仅是指针引用,增加了引用计数器,这样源头改变的时候,用这种retain方式声明的变量(无论被赋值的变量是可变的还是不可变的),它也会跟着改变;而copy声明的变量,它不会跟着源头改变,它实际上是深拷贝。
对源头是NSString的字符串,无论是retain声明的变量还是copy声明的变量,当第二次源头的字符串重新指向其它的地方的时候,它还是指向原来的最初的那个位置,也就是说其实二者都是指针引用,也就是浅拷贝。
另外说明一下,这两者对内存计数的影响都是一样的,都会增加内存引用计数,都需要在最后的时候做处理。
其实说白了,对字符串为啥要用这两种方式?我觉得还是一个安全问题,比如声明的一个NSString *str变量,然后把一个NSMutableString *mStr变量的赋值给它了,如果要求str跟着mStr变化,那么就用retain;如果str不能跟着mStr一起变化,那就用copy。而对于要把NSString类型的字符串赋值给str,那两都没啥区别。不会影响安全性,内存管理也一样。
其它相关文章: ios属性修饰符总结