iOS底层基础
1.iOS的内存管理
OC采用引用计数器对内存进行管理,当一个对象的引用计数(retainCount)为0,则被释放。
Objective C的引用计数理解起来很容易,当一个对象被持有的时候计数加一,不再被持有的时候引用计数减一,当引用计数为零的时候,说明这个对象已经无用了,则将其释放。
OC的内存机制可以简单概括为:谁持有(retain)谁释放(release)。retain引用计数+1,release反之。
采用引用计数的方式来管理内存,分MRC(手动引用计数)和ARC(自动引用计数)两种,其中ARC是基于MRC的.
在MRC中, 每当我们使用alloc,new,retain,copy使对象的引用计数增加,都要对应的使用release或者autorelease来使对象的引用计数减少,增加和减少的次数要相等.
内存管理的思考方式:
- 自己生成的对象,自己持有
- 非自己生成的对象,自己也能持有
- 不再需要自己持有对象时释放
- 非自己持有的对象无法释放
(1) 自己生成的对象,自己持有
在iOS内存管理中有四个关键字,alloc、new、copy、mutableCopy,自身使用这些关键字产生对象,那么自身就持有了对象
// 使用了alloc分配了内存,obj指向了对象,该对象本身引用计数为1,不需要retain
id obj = [[NSObject alloc] init];
// 使用了new分配了内存,objc指向了对象,该对象本身引用计数为1,不需要retain
id obj = [NSObject new];
(2) 非自己生成的对象,自己也能持有
// NSMutableArray通过类方法array产生了对象(并没有使用alloc、new、copy、mutableCopt来产生对象),因此该对象不属于obj自身产生的
// 因此,需要使用retain方法让对象计数器+1,从而obj可以持有该对象(尽管该对象不是他产生的)
id obj = [NSMutableArray array];
[obj retain];
(3) 不再需要自己持有对象时释放
id obj = [NSMutableArray array];
[obj retain];
// 当obj不在需要持有的对象,那么,obj应该发送release消息
[obj release];
(4) 无法释放非自己持有的对象
// 1. 释放一个已经释放的对象
id obj = [[NSObject alloc] init];
// 释放对象
[obj release];
// 释放了对象还进行释放
[obj release]; // 应用程序崩溃
//2. 释放一个不属于自己的对象
id obj1 = [obj object]; //取得的对象存在,但自己不持有对象
// obj1没有进行retain操作而进行release操作,使得obj持有对象释放,造成了野指针错误
[obj1 release];
在Objective C中,有三种类型是ARC适用的:
1.block
2.objective 对象,id, Class, NSError*等
3.由attribute((NSObject))标记的类型。
像double* ,CFStringRef等不是ARC适用的,仍然需要手动管理内存。
以CF开头的(Core Foundation)的对象往往需要手动管理内存。
2.iOS的强弱引用
2.1强弱引用的区别
简单说,强引用持有对象,而弱引用不持有对象。
具体看代码?
__strong NSObject *obj1=[[NSObject alloc] init];
__strong NSObject *obj2=obj1;
NSLog(@"%@,%@",obj1,obj2);
obj1=nil;
NSLog(@"%@,%@",obj1,obj2);
//输出 :
//<NSObject: 0x7fef53708b80>,<NSObject: 0x7fef53708b80>
//(null),<NSObject: 0x7fef53708b80>
将obj2声明改为__weak
__strong NSObject *obj1=[[NSObject alloc] init];
__weak NSObject *obj2=obj1;
NSLog(@"%@,%@",obj1,obj2);
obj1=nil;
NSLog(@"%@,%@",obj1,obj2);
//输出 :
//<NSObject: 0x7fef53708b80>,<NSObject: 0x7fef53708b80>
//(null),(null)
因为strong会使retaincount+1,所以第一部分代码retaincount是2,obj1置为nil之后retaincount-1,所以retaincount还剩1,因此不影响obj2;
而weak不会retaincount+1,所以整个代码段的retaincount一直是1,因此obj置为nil之后retaincount-1,所以retaincount为0,内存也跟着释放了,因此obj2也为空。
(这里提下weak和MRC时代的assign的区别,两者都可以用于弱引用,但是内存释放后使用weak会将对象置nil,而assign不会,会造成野指针,现在assign一般只用在基础类型))
2.2 iOS循环强引用
平时一般都是用strong,在造成循环引用的时候才会使用weak
当两个不同的对象各有一个强引用指向对方时就造成循环引用,会导致对象无法释放,例如我们常用的delegate,见图:
这就是我们在申明delegate时都是用weak的原因(MRC的话是用assign)
@property (nonatomic, weak) id<Delegate>delegate
block的使用也会照成循环引用,比如当一个对象持有block,而该block又持有该对象时,类似下面的伪代码会照成循环引用:
[self block:^{
self.value=1;
}];
应该改为
__weak typeof(self) weakself=self;
[self block:^{
weakself.value=1;
}];
注意:只有该对象持有block,而block里的代码块又持有该对象时才需要用到weak。:只有该对象持有block,而block里的代码块又持有该对象时才需要用到weak。
[UIView animateWithDuration:0.2 animations:^{
self.value=1;
}];
这种就不会造成循环引用。
luhh