定义:wiki
捋一捋ARC,或者说看看iOS的内存管理机制
引用计数:
引用计数是来用帮助oc进行内存回收的机制,如上图表示的代码类似于
NSObject *p1 = [NSObject new];
NSObject *p2 = p1;
NSObject *p3 = p1
指针指向了同一块内存区域,即对象存放的区域,用引用计数来描述的话,此刻对象的引用计数为3。
在引用计数为0时该内存区域将被回收用于存储其他。
MRC(manual reference counting)/MRR(manual retain-release):手动引用计数管理
既然是手动,说明引用计数不会像上面的代码一样NSObject *p2 = p1就让obj的引用计数加一。需要手动调用方法retain来增加引用计数
即 NSObject *p2 = [p1 retain];
相应的也不会在p2 = nil时减少引用计数,而需要调用release方法
即 [p2 release];
其他:
autorelease:调用后,对象会在当前的autoreleasepool结束时将引用计数减1
对象delloc:在dealloc下需要将不再被使用的指针调用release。
那么在ARC的时代下这些方法是怎么做到对开发者不可见的呢
ARC下系统会在编译时期自动加入对应的retain、release
可以考虑以下场景:
1.无返回值的方法:
ARC下文章开头的代码实际上是如下形式:
NSObject *p1 = [NSObject new];
NSObject *p2 = [p1 retain];
NSObject *p3 = [p1 retain]
//when no longer used
[p3 release];
[p2 release];
[p1 release];
2.有返回值的方法:
ARC下:
- (NSString *)genSomeObj
{
NSString *a = [[NSString alloc]initWithFormat:@"%@",@"ARC"];
return a;
}
MRC下:
- (NSString *)genSomeObj1
{
NSString *a = [[NSString alloc]initWithFormat:@"%@",@"ARC"];
return a;
}
- (NSString *)genSomeObj2
{
NSString *a = [[NSString alloc]initWithFormat:@"%@",@"ARC"];
return [a autorelease];
}
- (void)main
{
NSString *s1 = [self genSomeObj1];
[s1 release];
NSString *s2 = [self genSomeObj2];
//由于autorelease,不需要调用[s2 release]
}
ARC有一个规定:以new、alloc、copy、mutableCopy开头的方法需要由调用者主动管理内存
即同样是产生一个string的方法genSomeObj和newSomeObj,在ARC下被系统补充后的实现如下:
- (NSString *)genSomeObj
{
NSString *a = [[NSString alloc]initWithFormat:@"%@",@"ARC"];
return [a autorelease];
}
- (NSString *)newSomeObj
{
NSString *a = [[NSString alloc]initWithFormat:@"%@",@"ARC"];
return a;
}
调用newSomeObj方法的地方需要生成的指针不再使用时调用一次release(当然也是系统去调)
由于genSomeObj调用了autorelease,假设调用生成的指针还要继续指向该对象(需要保留该对象),那么还需要在此retain,例如
- (void)main
{
NSString *b = [[self genSomeObj]retain];
//do something with b
}
...
{
...
[b release];
...
}
3.dealloc
ARC的dealloc将调用实例变量的release(不同于MRC下开发可以随时release,所以MRC下之需要开发自己写未被release的对象即可)
4.ARC下提供的property:
property的目的是为实例变量提供了更方便的访问方式,系统会为声明为property的obj生成实例变量_obj,并且提供默认的getter\setter
假设是MRC的环境,setter会是怎样的呢?
- (void)setObj:(NSObject *)obj
{
[_obj release];
[obj retain];
_obj = obj;
}
为什么会这样?
1.实例变量_obj可能会有旧值,此时它需要指向新的对象地址的,所以需要将之前对象的引用计数减1(如果_obj之前指向了某个对象)
2.新对象马上要被_obj引用,所以需要obj增加引用计数
3.开始引用(赋值操作)
property是可以有好几种形容词的,说下最常见的nonatomic,strong,weak,assgin
nonatomic:字面意思表示对象操作不具有原子性,如果不加这个形容词,那么对象的访问会被加锁
strong:属性赋值时会自动retain
weak:弱引用不会增加引用计数、会在指向的对象将要销毁时自动置为nil,防止也指针,具体可见weak的详细介绍
assgin:一般用于修饰基本类型,如果修饰对象,是不会自动进行retain操作的(不加引用计数)
5.block的使用
block是c/c++里闭包的实现,关于闭包的理解见阮一峰的blog
block分为NSStackBlock\NSGlobalBlock\NSMallocBlock:栈block,全局block,堆bock,其中栈block存储于栈,其他存储于堆
一个block在创建时属于stackblock还是globalblock,取决于block内是否使用了外部变量,在没有使用外部变量时为globalblock,有globablock的好处是对于它的很多方法都不需要实际处理,例如copy操作。
但在ARC下其实很少会使用stackblock,只要将新创建的block赋值指针,ARC下会自动进行copy操作,stackblock即化身为mallocblock。
MRC下入若执行以下代码
@interface MRCObj()
{
void(^block)();
}
@end
@implementation MRCObj
- (void)test
{
NSInteger i= 0;
block = ^(){__unused NSInteger b = i;};
}
- (void)testBlock
{
block();
}
@end
//somewhere
{
MRCObj *mo = [MRCObj new];
[mo test];
[mo testBlock];
}
会由于block变量指向了一个stackblock,而方法结束后stackblock释放,再次调用则引起崩溃。
所以在MRC下则需要主动进行Block_copy、Block_release操作例如,ARC下则如上所述会自行拷贝
Tips:
亲自测试MRC运行情况可在Build Phase的Compile Sources内将该文件设为-fno-objc-arc