一、属性关键字分3类,原子性,引用计数,读写权限;
1.原子性:
·atomic:线程安全,开销大,影响性能,一般不用;
·nonatomic:常用,可以用代码,保证线程的安全;
2.引用计数:
·assign 修饰非指针变量,一般用于基础类型对象和C数据类型,这些都不是对象,由系统栈进行内存管理; (id类型的delegate属性到底是用assign还是weak?答案是weak,据说是weak比assign多了一个,当所指对象销毁时,自动置为nil,这样再给weak修饰的属性发送消息就不会crash) ;首先assign修饰的是基本数据类型,简单赋值不改变引用计数,weak只可以修饰oc中的对象。其次arc环境下weak修饰的对象被释放后指向对象的指针会被自动置为nil,而assign修饰的变量可能不会被置为nil,造成野指针会导致程序crash。
·weak对对象的弱引用,不增加对象的引用计数,也不持有该对象,当对象消失了,指针自动置为nil,从而避免野指针。
·strong对对象的强引用,增加对象的引用计数,持有该对象,如果指向的对象为空,会造成野指针;某些情况下,使用strong,也会造成循环引用,造成内存泄露;但是我们最常用的还是strong。
retain
retain用来释放旧的对象,将旧对象的值赋予输入对象,再提高输入对象的索引计数为1
对其他NSObject和其子类,对参数进行release旧值,再retain新值。
retain的语法为:
- (void)setName:(NSString *)newName {
if(name != newName) {
[name release];
name = [newName retain];
//注意: 把对象添加到数组中时,引用计数将增加对象的引用次数+1。
// name’s retain count has been bumped up by 1
}
声明属性时用strong或者retain效果是一样的(貌似更多开发者更倾向于用strong)。不过在声明Block时,使用strong和retain会有截然不同的效果。strong会等于copy,而retain竟然等于assign!
当然定义Block还是应该用copy(还有其他需要注意的地方,可以参考这篇文章:iOS: ARC和非ARC下使用Block属性的问题),因为非ARC下不copy的Block会在栈中,ARC中的Block都会在堆上的。
可以这样复现问题。在非ARC环境下,定义一个简单类型,定义一个Block属性,先用正确的copy:
参考:retain及区别
iOS的属性声明:retain和strong的区别 - 黑暗森林的歌者 - 博客园
·copy会创建一个引用计数为1的新对象,但并不持有该对象,只是在复制的时候将对象的值复制给该属性,使用copy关键字的对象必须要实现NSCopying协议。修饰的类型一般是NSString,NSArray,NSDictionary。建立一个索引计数为1的对象,然后释放旧对象。
·unsafe-unretained跟weak类似,都是弱引用,当所指对象的引用计数为0,即销毁时,指针不会自动置为nil。所以,会导致程序崩溃,现在一般都会使用weak。
3.读写权限:
readwrite: 可读可写,默认
readonly: 只可读,当属性可以被外界看到,但是却不想被外界修改;
1.1、根据代码分析:weak和strong
·声明2个属性
@property (nonatomic, strong) id strongObj;
@property (nonatomic, weak) id weakObj;
·测试代码
_strongObj = [NSObject new];
_weakObj = _strongObj;
NSLog(@"strongObj=%@, weakObj=%@", _strongObj, _weakObj);
打印结果 > strongObj=
假如我们给测试代码改为:
_strongObj = [NSObject new];
_weakObj = _strongObj;
_strongObj = nil; // 置为nil
NSLog(@"strongObj=%@, weakObj=%@", _strongObj, _weakObj);
打印结果: strongObj=(null), weakObj=(null)
由以上两个打印结果可以看出,weak为弱引用,并没有真正的持有该对象,也没有对该引用计数产生影响,当所指的对象不存在时,weak指针就自动置为nil。
再次修改测试代码,来分析分析strong
id obj = [NSObject new];
_strongObj = obj;
_weakObj = _strongObj;
obj = nil;
NSLog(@"strongObj=%@, weakObj=%@, obj=%@", _strongObj, _weakObj,obj);
打印结果: strongObj=
可以看到,虽然给obj置为nil的时候,strong属性和weak修饰的属性都可以打印出结果,由此可说明,strong对对象强引用,并导致引用计数+1,所以,只有再把_strongObj = nil,该对象的引用计数才会变为0, _strongObjc=null,_weakObj=null;
1.2·根据代码分析:copy,以及在声明block属性的时候为什么一定要用copy
id obj = [NSObject new];
_copOBJ = obj;
NSLog(@"copyObj=%@", _copObj);
obj = [NSObject new];
NSLog(@"copyObj=%@", _copObj);
打印结果:
copyObj=
copyObj=
由此看出,原对象虽然更改了,但是并未影响copy修饰的属性,即深拷贝。
block经常使用copy关键字 原因:见下次详解
注意:NSMutableString、NSMutableArray、NSMutableDictionary,使用copy应注意 原因:添加,删除,修改数组内的元素的时候,程序会因为找不到对应的方法而崩溃.因为copy就是复制一个不可变NSArray的对象;
内存管理的好处说:iOS内存管理机制的好处就是为了让开发人员方便的管理内存,减少程序中的内存泄漏,在内存管理难度与性能之间找一个最佳的平衡点。
其他:
二、关于MRC:
MRC黄金法则:凡是使用alloc、init、copy、nsmutablecopy、retain进行创建对象的都要使用release或者autorelease进行释放;
谁创建,谁销毁。谁引用,谁管理。
三、一些点
Objective-C 编程:iOS 中的深拷贝与浅拷贝 -
1 深拷贝 浅拷贝
1.1. 浅拷贝
浅拷贝就是对内存地址的复制,让【目标对象指针】和【源对象指针】指向同一块内存空间,当内存被销毁时,指向这块内存的所有指针需要重新定义才可以使用,要不然会成为野指针。
浅拷贝就是拷贝【指向原来对象的】指针,使原对象的引用计数+1,可以理解为创建了一个指向原对象的新指针,并没有创建一个全新的对象。
1.2. 深拷贝
深拷贝是指拷贝对象的具体内容,而内存地址是自主分配的,拷贝结束之后,两个对象虽然存的值是相同的,但是内存地址不一样,两个对象也互不影响,互不干涉。
深拷贝就是创建【值与原对象相同,但是内存地址不同的】新对象,创建后的对象和原对象没有任何关系。
总结
浅拷贝是指针拷贝,深拷贝是内容拷贝。本质区别在于:
是否影响内存地址的引用计数;
是否创建新的内存地址。
2、
2.1
弱引用: 不持有对象
强引用:持有对象
2.2
常量: 不变的量,其值不能被改变,利用const修饰的变量为常量,不可修改; 利用define定义的一般为常量 ,定义不用分号; 利用extern修饰的量只是生命,
指针变量: 存放地址的变量
非指针常量: 不带有指针的常量,对于指针,&p传递的是指针的地址; p传递的是指针所指的变量的地址,也就是变量的地址。
形参: 形式参数,本质上一个名称而已,参与值的传递
实参:可以是常量 变量 表达式, 有实际意义的值,参与方法执行
2.3 到底什么时候才需要在ObjC的Block中使用weakSelf/strongSelf - 浮生猎趣
在 doSomething 中,weakSelf 不会变成 nil,不过在 doSomething 执行完成,调用第二个方法 doOtherThing 的时候,weakSelf 有可能被释放,于是,strongSelf 就派上用场了:
__strong 确保在 Block 内,strongSelf 不会被释放。
在 Block 内如果需要访问 self 的方法、变量,建议使用 weakSelf。
如果在 Block 内需要多次 访问 self,则需要使用 strongSelf。