一、@property
这个关键词的唯一作用就是声明getter、setter方法接口。
二、@synthesize
实现setter、getter方法,找不到实例变量则主动创建一个。
三、nonatomic与atomic
atomic原子操作,提供多线程安全,setter方法会发生变化;基本来讲是防止在写未完成的时候被另外一个线程读取,造成数据错误,但是这种操作的弊端就是非常消耗内存资源,苹果手机的内存资源相当宝贵,所以没有使用多线程间的通讯编程,用nonatomic是一个不错的选择。
指出访问器不是原子操作,而默认地,访问器是原子操作。也就是说
,在多线程环境下,解析的访问器提过一个队属性的安全访问,从获取器得到的返回值或者通过设置器设置的值可以一次完成,即便是别的线程也正在对其进行方未能。如果没有指定nonatomic,在自己管理内存的环境中,解析的访问器保留并自动释放返回的值,如果指定了nonatomic,那么访问器只是简单的返回这个值。
四、assign、retain、copy,这是三个关键词得好好说一下。
如:NSString * house = [[NSString alloc] initWithFormat:@"The house is mine"];
首先在堆上分配一段内存来存储@"The house is mine", 假设内存地址为:0X000 内容则为:@"The house is mine"
然后在栈上分配一段内存存储house,假设地址为0XFFFF,内容就是0X0000。
1、assign:NSString * myHouse = [house assign];
此时house和myHouse完全相同,地址都为0XFFFF,内容为0X0000,也就是说myHouse只是house的一个别名,对任何一个操作就等于对另一个操作,因此retainCount不需要增加。
所有属性都 默认 assign ,通常用于标量(简单变量 int , float , CGRect 等)
一种典型情况是用在对对象没有所有权的时候,通常是 delegate ,避免造成死循环(如果用 retain 的话会死循环)
2、retain:NSString * myHouse = [house retain];
此时myHouse的地址不再是0XFFFF,可能是0XEEEE,但内容为0X0000,所以说house和myHouse都可以管理"The house is mine"所在的内存,因此retainCount需要增加1,
3、copy:NSString * myHouse = [house copy];
此时会在堆上重新开辟一段内存存放@"The house is mine",假设内存地址为:0X1111,内容则为:@"The house is mine"
同时会在栈上为myHouse分配内存,假设地址为:0XBBBB,内容为:0X1111,也就是说myHouse是一个新的对象,与house没有任何关系,很明显,retainCount增加1,
属性必须是 objc 对象,拥有对象所有权,必须在 dealloc 中 release 一次。且属性必须实现 NSCopying 协议
一般常用于 NSString 类型
五、strong与weak
参考文献: iOS ARC 完全指南
提示:本文中所说的"实例变量"即是"成员变量","局部变量"即是"本地变量"
5-1、简介
ARC是自iOS 5之后增加的新特性,完全消除了手动管理内存的烦琐,编译器会自动在适当的地方插入适当的retain、release、autorelease语句。你不再需要担心内存管理,因为编译器为你处理了一切
注意:ARC 是编译器特性,而不是 iOS 运行时特性(除了weak指针系统),它也不是类似于其它语言中的垃圾收集器。因此 ARC 和手动内存管理性能是一样的,有时还能更加快速,因为编译器还可以执行某些优化
5-2、原理
ARC 的规则非常简单:只要还有一个变量指向对象,对象就会保持在内存中。当指针指向新值,或者指针不再存在时,相关联的对象就会自动释放。这条规则对于实例变量、synthesize属性、局部变量都是适用的
5-3、strong指针
控制器中有个文本输入框框属性
- @property (nonatomic, assign) IBOutlet UITextField *nameField;
1.如果用户在文本框中输入mj这个字符串
那么就可以说,nameField的text属性是NSString对象的指针,也就是拥有者,该对象保存了文本输入框的内容
2.如果执行了如下代码
- NSString *name = self.nameField.text;
一个对象可以有多个拥有者,在上面代码中,name变量同样也是这个NSString对象的拥有者,也就是有两个指针指向同一个对象
3.随后用户改变了输入框的内容,比如
此时nameFeild的text属性就指向了新的NSString对象。但原来的NSString对象仍然还有一个所有者(name变量),因此会继续保留在内存中
4.当name变量获得新值,或者不再存在时(如局部变量方法返回时、实例变量对象释放时),原先的NSString对象就不再拥有任何所有者,retain计数降为0,这时对象会被释放
如,给name变量赋予一个新值
- name = @;
我们称name和nameField.text指针为"Strong指针",因为它们能够保持对象的生命。默认所有实例变量和局部变量都是Strong指针
5-4、weak指针
weak型的指针变量仍然可以指向一个对象,但不属于对象的拥有者
1.执行下面的代码
- __weak NSString *name = self.nameField.text;
name变量和nameField.text属性都指向同一个NSString对象,但name不是拥有者
2.如果文本框的内容发生变化,则原先的NSString对象就没有拥有者,会被释放,此时name变量会自动变成nil,称为空指针
weak型的指针变量自动变为nil是非常方便的,这样阻止了weak指针继续指向已释放对象,避免了野指针的产生,不然会导致非常难于寻找的Bug,空指针消除了类似的问题
3.weak指针主要用于“父-子”关系,父亲拥有一个儿子的strong指针,因此父亲是儿子的所有者;但为了阻止所有权循环,儿子需要使用weak指针指向父亲。典型例子是delegate模式,你的ViewController通过strong指针(self.view)拥有一个UITableView, UITableView的dataSource和delegate都是weak指针,指向你的ViewController
5-5、strong和weak指针的使用注意
1.下面代码是有问题的:
- __weak NSString *str = [[NSString alloc] initWithFormat:@];
- "%@"
str是个weak指针,所以NSString对象没有拥有者,在创建之后就会被立即释放。Xcode还会给出警告("Warning: Assigning retained object to weak variable; object will be released after assignment")
2.一般的指针变量默认就是strong类型的,因此一般我们对于strong变量不加__strong修饰,以下两行代码是等价的:
- NSString *name = self.nameField.text;
- __strong NSString *name = self.nameField.text;
3.属性可以是strong或weak,写法如下
- weak一般应用: UI控件
- strong一般应用: OC对象类型(NSArray、NSDate、NSNumber、模型类)一个对象只要有强指针引用着,就不会被销毁
- @property @property (nonatomic, weak) id delegate;
4.以下代码在ARC之前是可能会行不通的,因为在手动内存管理中,从NSArray中移除一个对象时,这个对象会发送一条release消息,可能会被立即释放。随后NSLog()打印该对象就会导致应用崩溃
- id obj = [array objectAtIndex:];
- ];
- "%@", obj);
在ARC中这段代码是完全合法的,因为obj变量是一个strong指针,它成为了对象的拥有者,从NSArray中移除该对象也不会导致对象被释放
四.属性( @property )与成员变量的那些事 :
- 属性对成员变量扩充了存取方法 .
- 属性默认会生成带下划线的成员变量 .
- 早期的 Xcode 不支持自动合成成员变量的存取方法 , 所以古老的iOS工程师是愤怒的 .
- 后来 Xcode 智能了一点 , 可以用 @synthesize 关键字自动合成成员变量的存取方法 , 此时的iOS工程师是郁闷的 .
- 现在 Xcode 会在我们声明属性时自动合成存取方法 , 连@synthesize都不用写了 , 这样iOS工程师彻底解放了 .
- 顺便提一下 @dynamic , 这个关键字是告诉编译器存取方法在运行时会有的 . 也可以说 @dynamic 是工程师自己来实现成员变量的存取方法 , @synthesize 是让 Xcode 帮你生成存取方法 .
属性中的修饰词 - 我的理解 :
-
assign ( ARC/MRC )
1.这个修饰词是直接赋值的意思 , 整型/浮点型等数据类型都用这个词修饰 .
2.如果没有使用 weak strong retain copy 修饰 , 那么默认就是使用 assign 了. ( 它们之间是有你没我的关系 )
3.当然其实对象也可以用 assign 修饰 , 只是对象的计数器不会+1 . ( 与 strong 的区别 )
4.如果用来修饰对象属性 , 那么当对象被销毁后指针是不会指向 nil 的 . 所以会出现野指针错误 . ( 与weak的区别 ) -
weak ( ARC )
1.弱指针是针对对象的修饰词 , 就是说它不能修饰基本数据类型 .
2.weak 修饰的对象计数器不会+1 , 也就是直接赋值 .
3.弱引用是为打破循环引用而生的 .
4.它最被人所喜欢的原因是 它所指向的对象如果被销毁 , 它会指向 nil . 而 nil 访问什么鬼都不会报野指针错误 . -
strong ( ARC )
1.直接赋值并且计数器 +1 .
2.在 ARC 里替代了 retain 的作用 . -
retain ( MRC )
1.release 旧对象( 旧对象计数器 -1 ) , retain 新对象( 新对象计数器 +1 ) , 然后指向新对象 .
2.在set方法里面是这样的 :if (_dog) { [_dog release]; } _dog = [dog retain];
-
copy ( ARC/MRC )
1.copy 在 MRC 时是这样做的 release 旧对象( 旧对象计数器 -1 ) , copy 新对象( 新对象计数器 +1 ) , 然后指向新对象 .(新对象是指最终指向的那个对象,不管深拷贝还是浅拷贝)
1.1在set方法里面是这样的 :if (_dog) { [_dog release]; } _dog = [dog copy];
2.copy 在 ARC 时是这么干的 copy 新对象( 新对象计数器 +1 ) , 然后指向新对象 .
2.1在set方法里面是这样的 :_dog = [dog copy];
3.使用注意 :
3.1 修饰的属性本身要不可变的 . 例如 NSMutableArray 采用 copy 修饰 , 添加元素表面上可以 一到运行就崩溃了 , 因为 copy 过后实际上成了NSArray了 . ( 队友 , 我们不吭你 )
3.2 遵守 NSCopying 协议的对象使用 . -
nonatomic ( ARC/MRC )
1.不对set方法加锁 .
2.性能好
3.线程不安全 -
atomic ( ARC/MRC )
1.原子属性就是对生成的 set 方法加互斥锁 @synchronized(锁对象) .
@synchronized(self) { _delegate = delegate; }
2.需要消耗系统资源 .
3.互斥锁是利用线程同步实现的 , 意在保证同一时间只有一个线程调用 set 方法 .
4.其实还有 get 方法 , 要是同时 set 和 get 一起调用还是会有问题的 . 所以即使用了 atomic 修饰 还是不够安全 . -
readonly
1.让 Xcode 只生成get方法 .
2.不想把暴露的属性被人随便替换时 , 可以使用 . -
readwrite
1.让 Xcode 生成get/set方法 .
2.不用 readonly 修饰时 , 默认就是 readwrite . -
getter/setter 的自定义方法名 .
1.一般对于 有/无 是/否 等这样的属性 , getter 方法名前面加个 is 会显得通俗易懂 .
@property (nonatomic, getter = isFinish, setter = setFinish) BOOL finish;