所谓的循环引用是:当A对象里面强引用了B对象,B对象又强引用了A对象,这样两者的RetainCount值一直无法为0,于是内存时钟无法释放,导致内存泄漏。所谓的内存泄漏就是本应该释放的对象,在其生命周期结束之后依旧存在。这种循环还能存在于3,4……个对象之间,只要相互形成环,就会导致Retain Cicle的问题。
还有一种自身引用自身的,当一个对象内部的一个obj,强引用的自身,也会导致循环引用的问题出现。常见的就是Block里面引用的问题
平常我们常见的循环引用有block循环引用和对象之间相互强引用两种情况;
1、block循环引用问题:例如
#import "Person.h"
typedef void(^PersonBlock)(NSString *name);
@interface Person()
@property (nonatomic,strong)NSArray *propertyS;
@property (nonatomic,strong)PersonBlock block;
@end
@implementation Person
- (id)init
{
if (self = [superinit]) {
self.propertyS =@[@"女",@"23"];
self.block = ^(NSString *name){
NSLog(@"arr:%@",self.propertyS);
};
}
return self;
}
@end
其中在block中调用propertyS会有警告出现,这是因为在block中调用了self所拥有的东西,引起了循环引用,那么这个循环引用具体怎样引起的呢?
(1)block中引用了self,self被block retain,propertyS又retain了该block的一个拷贝
(2)propertyS是在self中定义赋值的,因此是被self retain的
因此形成了循环引用,不会调用dealloc
解决方法是利用__weak或者__unsafe__unretained去修饰self(在ARC模式下),在MRC模式下用__block修饰
2、两个对象之间相互强引用:
@interface Person : NSObject
{
Person *person1;
Person *person2;
NSArray *array;
}
@property (readwrite,assign)Person *person1;
@property (readonly)Person *person2;
@end
person拥有两个Person对象,每个对象又都拥有一个NSArray对象。属性person1和person2都指向了彼此的Person对象。那么两个对象间形成了强引用,ARC将不会销毁这个对象。
为了阻止这种事的发生,声明其中一个属性为弱引用即可(__weak Person *person2)。这样,当person2指向空并且Person对象超出范围时,ARC可以安全的给这个对象发送一个release消息。
__weak指示符既可以声明弱引用,也可以声明空引用。__unsafe_unretained声明弱引用,不是空引用。如果要用后者,那么要自己处理空引用,否则会内存泄漏
以下内容转载自:http://www.jianshu.com/p/701da54bd78c
ARC环境下,所有权修饰符:
1、__strong
2、__weak
3、__unsafe_unretained
4、__autoreleasing
一般我们如果不写,默认的修饰符是__strong
1、__strong的实现原理
(1) 首先我们先来看看生成的对象持有自己的情况,利用alloc/new/copy/mutableCopy生成对象。当我们声明了一个__strong对象
id __strong obj = [[NSObject alloc] init];
LLVM编译器会把上述代码转换成下面的样子:
`id __attribute__((objc_ownership(strong))) obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));`
相应的会调用
id obj = objc_msgSend(NSObject, @selector(alloc));
objc_msgSend(obj,selector(init));
objc_release(obj);
在ARC有效的时候就会自动插入release代码,在作用域结束的时候自动释放。
(2)对象不持有自己
生成对象的时候不用alloc/new/copy/mutableCopy等方法。例如:
id __strong obj = [NSMutableArray array];
LLVM编译器会把上述代码转换成下面的样子:
id __attribute__((objc_ownership(strong))) array = ((NSMutableArray *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("array"));
相应的会调用
id obj = objc_msgSend(NSMutableArray, @selector(array));
objc_retainAutoreleasedReturnValue(obj);
objc_release(obj);
与之前对象会持有自己的情况不同,这里多了一个objc_retainAutoreleasedReturnValue函数。
objc_retainAutoreleasedReturnValue函数是用于自己持有(retain)对象的函数,它持有的对象应为返回注册在autoreleasepool中对象的方法或者是函数的返回值。
在ARC中原本对象生成之后是要注册到autoreleasepool中,但是调用了objc_autoreleasedReturnValue 之后,紧接着调用了 objc_retainAutoreleasedReturnValue,objc_autoreleasedReturnValue函数会去检查该函数方法或者函数调用方的执行命令列表,如果里面有objc_retainAutoreleasedReturnValue()方法,那么该对象就直接返回给方法或者函数的调用方。达到了即使对象不注册到autoreleasepool中,也可以返回拿到相应的对象。
2.__weak的实现原理
声明一个__weak对象
id __weak obj = strongObj;
假设这里的strongObj是一个已经声明好了的对象
LLVM转换成对应的代码
id attribute((objc_ownership(none))) obj1 = strongObj;
相应的会调用
id obj ;
objc_initWeak(&obj,strongObj);
objc_destoryWeak(&obj);
objc_initWeak的实现其实是这样的
id objc_initWeak(id *object, id value) {
*object = nil;
return objc_storeWeak(object, value);
}
会把传入的object变成0或者nil,然后执行objc_storeWeak函数。
objc_destoryWeak函数的实现
void objc_destroyWeak(id *object) {
objc_storeWeak(object, nil);
}
objc_initWeak和objc_destroyWeak函数都会去调用objc_storeWeak函数,唯一不同的是调用的入参不同,一个是value,一个是nil。
objc_storeWeak函数就把第一个入参的变量地址注册到weak表中,然后根据第二个入参来决定是否移除。如果第二个参数为0,那么就把__weak变量从weak表中删除记录,并从引用计数表中删除对应的键值记录。
所以如果__weak引用的原对象如果被释放了,那么对应的__weak对象就会被指为nil。原来就是通过objc_storeWeak函数这些函数来实现的。