我们先来看一下基本的修饰符
atomic
设置@property属性时,默认为atomic,提供多线程安全
在多线程环境下,原子操作是必要的,否则有可能引起错误的结果。加了atomic,setter函数会变成这样:
{lock}
if (property != newValue)
{ [property release];
property = [newValue retain]; }
{unlock}
对setter方法加了互斥锁@synchronzied(锁对象)
nonatomic
禁止多线程,变量保护,提高性能
atomic是Objc使用的一种线程保护技术,基本上来讲,是防止在写未完成的时候被另外一个线程读取,从而造成数据错误。而这种机制是耗费系统自痛的,所以在iPhone这种小型设备上,如果没有使用多线程之间通讯编程,那么最好使用nonatomic
assign
对基础数据类型(NSInteger,CGFloat)和C数据类型(int,float)等
- assign在ARC和MRC中都是存在的
- assign一般用来修饰基本数据类型
- assign也可用来修饰对象,但是,对象的引用计数不+1
- assign如果用来修饰对象属性,当对象销毁后之后指针不会指向nil,会出现野指针错误(与weak的区别)
- MRC用assign来修饰代理,是为了防止循环引用
weak
- 弱指针是针对对象的修饰词,就是说他不能修饰基本数据类型
retain
对其他NSObject和其子类属性进行release旧值,再retain新值
指定retain会在赋值时唤醒传入值的retain消息,此属性只能用于OC对象类型,而不能用于Core Foundation对象,原因在于retain操作会增加对象的引用计数+1,而基本数据类型和Core Foundation对象都没有引用计数。在非ARC环境下,你需要自己retain一个想要保持的对象。ARC环境下就不需要了。现在唯一要做的就是用一个指针指向这个对象,只要指针没有被重置为空,对象就会一直在堆上。当指针指向新值的时候,原来的对象就会被release一次。这对实例变量,synthesize的变量或者是局部变量都是实用的。
copy
一般NSString或者block用copy修饰
首先, block是一个对象, 所以block理论上是可以retain/release的. 但是block在创建的时候它的内存是默认是分配在栈(stack)上, 而不是堆(heap)上的. 所以它的作用域仅限创建时候的当前上下文(函数, 方法...), 当你在该作用域外调用该block时, 程序就会崩溃.
Apple官方文档
意思就是 : 一般情况下你不需要自行调用copy或者retain一个block. 只有当你需要在block定义域以外的地方使用时才需要copy. Copy将block从内存栈区移到堆区.
其实block使用copy是MRC留下来的也算是一个传统吧, 在MRC下, 如上述, 在方法中的block创建在栈区, 使用copy就能把他放到堆区, 这样在作用域外调用该block程序就不会崩溃. 但在ARC下, 使用copy与strong其实都一样, 因为block的retain就是用copy来实现的
。
copy 是对引用对象进行copy操作,生成了一个新的对象,新对象的引用计数为1,原来的引用对象引用计数不变,这里我们不难发现copy和retain的区别:
copy其实是建立了一个全新的对象,原来对象的引用计数不变,retain是对原有的对象再次的强引用,对象引用计数+1
举例说明:
1.比如一个NSString 对象,地址为0×1111 ,内容为@”STR”,Copy 到另外一个NSString 之后,地址为0×2222 ,内容相同。
2.新的对象retain为1 ,旧有对象没有变化retain 到另外一个NSString 之后,地址相同(建立一个指针,指针拷贝),内容当然相同,这个对象的retain值+1。
总结:retain 是指针拷贝,copy 是内容拷贝。
大家可以打印对象的地址和变量自身的地址
NSString *mstr = @"123";//如果字符串是NSString类型,copy还是strong 他返回的都是不可变类型,如果是NSMutableString类型,对其中任何一个重新赋值,会产生新的内存地址,所以他改变,对另外的对象没有影响,用strong是对其指针再次引用,retainCount+1,用copy则是深复制,和源头互相独立,互不影响
self.rstr = mstr;
self.cstr = mstr;
//基础数据类型在栈上,对象在堆上
NSLog(@"mStr:%p,%p", mstr,&mstr);
NSLog(@"copyStr:%p,%p", _cstr, &_cstr);
NSLog(@"retainStr:%p,%p", _rstr, &_rstr);
NSLog(@"%@=========%@========%@",mstr,self.rstr,self.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 = @"234";//重新赋值,会发现mstr本身在内存中的地址没有变,还是指向mstr,但是他的值地址发生改变了,
NSLog(@"mStr:%p,%p", mstr,&mstr);
NSLog(@"copyStr:%p,%p", _cstr, &_cstr);//前一个打印的地址是cstr这个变量的 值 的内存地址,即对值的引用,后一个打印的地址是cstr这个变量自身的内存地址
NSLog(@"retainStr:%p,%p", _rstr, &_rstr);
NSLog(@"%@=========%@========%@",mstr,self.rstr,self.cstr);
这里来解释一下为什么字符串用copy而不用strong
对于字符串,我们希望是一次内容的拷贝,也就是说拷贝出来的字符串对象和愿字符串对象相互独立,没有任何关系,这样,外部修改拷贝的字符串也就不会影响我们原来的字符串的值
const
变量修饰符,只有只读权限
register
变量名前加register修饰,编译器会优先将此变量放在寄存器中,这样对这个变量进行的各种操作及运算会很快。当然CPU周边的用户可用空闲寄存器是有限的,所以当定义多个register修饰的变量后,当可用寄存器用完之后,依然是在普通的内存中为变量开辟空间
static
有时候我们非常希望,用一个小变量记录某函数调用的次数,解决这个问题
方法1.可以使用全局变量,但是由于所有函数都可以修改他,对于较大程序出了问题不好调试,所以使用全局变量并不好
方法2.可以在目标函数中定义一个static变量,每次进入函数让其++,static作用在局部变量前,函数结束此变量的值不清空,即改变了此变量的生命期,而且直到整个程序结束,并且此变量的值只有在定义他的函数体内被使用和重新赋值
一般有如下作用:
- 函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只分配一次,因此其值在下次调用时仍维持上次的值;
- 在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外的其他函数访问
- 在模块内的static函数只可被这一模块内的其他函数调用,这个函数的使用范围被限制在声明他的模块内
- 在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝
- 在类中的static成员函数属于整个类所有,这个函数不接受this指针,因而只能访问类的static成员变量
extern
extern int n;
extern可以扩展全局变量或函数的作用范围,只有全局变量或函数才可以用extern扩展
extern可以扩展函数的作用范围,可以跨文件扩展,前提是被扩展的全局变量或者函数在定义时没有被statci修饰
扩展
1.ARC环境下。strong代替了retain,weak代替assign
2.weak的作用,在ARC环境下,所有指向这个对象的weak指针都将被置为nil,。这个特性很有用,相信很多开发者都被指针指向已释放的对象所造成的EXC_BAD_ACCESS困扰过,使用ARC以后,不论是strong还是weak类型的指针,都不会再指向一个已经销毁的对象,从根本上解决了意外释放导致的crash
3.assign的作用,简单赋值(指针赋值),不改变引用计数,对基础数据类型(NSIntefer,CGFloat)和C数据类型(int,float,double,char)等,
4.strong的作用,在ARC环境下,只要某一个对象被一个strong指针指向,该对象就不会被销毁,如果对象没有被任何strong指针指向,那么就会被销毁,在默认情况下,所有的实例变量和局部变量都是strong类型
5.__weak, __unsafe_unretained, __autoreleasing,的区别
__weak:声明了一个可以自动 nil 化的弱引用。 __unsafe_unretained:声明一个弱应用,但是不会自动nil化,也就是说,如果所指向的内存区域被释放了,这个指针就是一个野指针了。 __autoreleasing:用来修饰一个函数的参数,这个参数会在函数返回的时候被自动释放。
自动引用计数
Automatic Reference Counting (ARC) is a compiler feature that provides automatic memory management of Objective-C objects. Rather than having to think about retain and release operations, ARC allows you to concentrate on the interesting code, the object graphs, and the relationships between objects in your application.
ARC是一个编译器特性,提供了对iOS对象的自动内存管理,ARC在编译期间自动在适当的地方添加Objc对象的retain和release操作代码,而不需要我们去关心。从概念上来说,ARC与手动计数内存管理遵循相同的内存管理法则,但是ARC也无法防止循环强引用
ARC引入了新的修饰符来修饰变量和声明变量
- 声明变量的修饰符:__strong,__weak,__unsafe_unretained,__autoreleasing;
- 声明属性的修饰符:strong,weak,unsafe_unretained
对象和Core Foundation-style对象直接的转换修饰符符号:__bridge,__bridge_retained或CFBridgingRetain,__bridge_transfer或CFBridgingRelease - 对于线程的安全,有nonatomic,这样效率就更高了,但是不是线程的,如果要线程安全,可以使用atomic,这样在访问时会有线程锁
- 记住内存管理法则:谁使对象的引用计数+1,不再引用时,谁就负责将该对象的引用计数-1
参考资料
[http://blog.csdn.net/woaifen3344/article/details/50219177]
[http://blog.csdn.net/u011366778/article/details/47088251]
[http://blog.csdn.net/ios_hc/article/details/51933174]
网友评论