内存管理-ARC
1、ARC内存管理本质
OC的内存并不是在程序运行时监控使用的内存空间释放不需要使用的,而是在编译过程中由编译器在合适的位置添加内存管理代码,MRC机制下需要自己添加。本质上还是和C语言一样使用malloc申请空间,free释放空间,只不过是由系统添加。
2、对象所有权修饰符
四种修饰符__strong、__weak、__unsafe_retained、__autoreleasing
__strong:
使用__strong修饰的指针,会持有所指向的对象,即编译器会调用retain方法;当指针置为nil时,编译器会调用release方法。
默认在创建指针但没有显示指明所有权修饰符的情况下,使用__strong修饰。
// ARC
Person * __strong p = [[Person alloc] init];
p = nil;
// MRC
Person *p = [[Person alloc] init];
[p retain];
[p release];
__weak:
使用__weak修饰的指针,不会持有所指向的对象,即编译器不会调用retain方法;当指针置为nil时,编译器不会调用release方法。
当对象没有持有者,即引用计数为0时,__weak修饰的指针会被编译器置为nil。
一般使用__weak来解决循环引用的问题。
Person * __strong p = [[Person alloc] init];
Person * __weak p1 = p;
p = nil; // 此时p1也被编译器置为nil。
循环引用出现的条件:
1.只有使用堆的数据结构才能发生循环引用。
2.彼此都有属性指向对方。即对象中存在指向对方的属性。
-------------Person-----------------
@interface Person : NSObject
@property (nonatomic, strong) Man *man;
@end
--------------Man---------------------
@interface Man : NSObject
@property (nonatomic, strong) Person *person;
@end
---------------main-------------------
void main(){
Person *p = [[Person alloc] init];
Man *m = [[Man alloc] init];
p.man = m;
m.person = p;
//这样就满足出现循环引用的两个条件,都是保存在堆中,对象中存在指向对方的属性。
}
block这种数据结构,在ARC下使用block外部的变量会保存到堆中,如果使用的外部变量是对象时,会强引用该对象,形成上面的两个条件。
__unsafe_retained:
iOS5推出ARC时,为了兼容低版本中无弱引用,推出了__unsafe_retained,和weak的功能一样,只是在指向的对象释放时,指针不会被置为nil,造成野指针的现象。即使用__unsafe_retained修饰的指针不会被编译器管理,weak指针在对象释放时置为nil的动作时编译器添加的代码。
__autoreleasing:
用__autoreleasing修饰的变量,将对象赋值给该变量时,会自动将该变量加入到自动释放池。
Person * __autoreleasing p = [[Person alloc] init]; // Person对象被自动添加到自动释放池中,相当于MRC下调用autorlease方法。
等同于Person * __strong p = [Person person];
函数或方法的参数和返回值一般用到自动释放池。
+(id)person
{
Person *p = [[Person alloc]init];
return p;
}
由于p变量的作用域在person方法内,当方法返回时p变量被销毁,为了防止Person对象被销毁,会将Person对象添加到自动释放池中。
+(void)createPerson:(Person **p) // 隐藏了__autoreleasing,实际上为+(void)createPerson:(Person * __autoreleasing *p);
{
*p = [[Person alloc] init];
}
Person对象会被添加到自动释放池中。p指针在ARC下的内存管理为 “非自己创建并引用对象”。
+(void)createPerson:(Person * __strong *p) // 显示声明为__strong,这样变量在ARC下的内存管理为 "自己创建并持有对象",对象不会加入到自动释放池
{
*p = [[Person alloc] init];
}
Person *p和Person **p1的所有权修饰符:
p的修饰符为__strong,那么保存&p的变量的修饰符必须是__strong。即Person * __strong *pp = &p;
Person **p1(其实显示的写法为Person * __autoreleasing * p1),因此p1 = &p;这样写编译器会报错。p为__strong而p1为__autoreleasing。
对象指针和指向该对象指针的指针的所有权修饰符必须一致。
Person * __weak p = nil;
Person * __weak * p1 = &p;
Person * __unsafe_retained p = nil;
Person * __unsafe_retained p1 = &p;