循环引用由来 俩个对象相互强引用,俩个对象的retainCount一直无法为0,内存无法释放造成内存泄漏; 多个对象同理;
一. __strong __weak实现原理
所有权修饰:符:__strong,__weak,__unsafe_unretained,__autoreleasing; 所有权修饰符默认不写是__strong
__strong示例
对象持有自己 生成的时候用alloc/new/copy/mutableCopy
声明一个__strong对象
1
2
3
{
id __strong obj = [[NSObject alloc] init]
}
LLVM编译器转换为
1
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"));
简单的调用
1
2
3
id obj = objc_msgSend(NSObject , @selector(alloc ))
objc_msgSend(obj ,selector(init ))
objc_release(obj )
由于在ARC下,会自动插入release代码,所以不难理解
对象不持有自己 生成的时候不使用alloc/new/copy/mutableCopy 例如使用类方法
1
2
3
{
id __strong obj = [NSArray array];
}
LLVM编译器转换为
1
id __attribute__((objc_ownership (strong ))) array = ((NSMutableArray *(*)(id , SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("array"));
简单的调用
1
2
3
id obj = objc_msgSend(NSMutableArray , @selector(array ))
objc_retainAutoreleasedReturnValue(obj )
objc_release(obj )
相比之前的对象持有自己的情况不同,此处多出了一个objc_retainAutoreleasedReturnValue函数
此处有三个函数需要说明(文档的8.13,8.14,8.15)
1.id objc_retainAutoRelease(id value)
描述
If value is null, this call has no effect. Otherwise, it performs a retain operation followed by an autorelease operation.
实现
id objc_retainAutorelease(id value) {return objc_autorelease(objc_retain(value));}
2.id objc_retainAutoReleaseReturnValue(id value)
描述
If value is null, this call has no effect. Otherwise, it performs a retain operation followed by the operation described in objc_autoreleaseReturnValue
实现
id objc_retainAutoreleaseReturnValue(id value) {return objc_autoreleaseReturnValue(objc_retain(value));}
3.id objc_retainAutoReleasedReturnValue(id value)
描述
If value is null, this call has no effect. Otherwise, it attempts to accept a hand off of a retain count from a call to objc_autoreleaseReturnValue on value in a recently-called function or something it calls. If that fails, it performs a retain operation exactly like objc_retain.
这个函数用于自己持有对象的函数(retain),它持有的对象返回注册在autoreleasepool中的对象非的方法或者是函数的返回值
ARC中原本对象的生成是要注册到autoreleasepool中,但是调用了id objc_autoReleaseReturnValue(id value),又紧接着调用了id objc_retainAutoReleasedReturnValue(id value),那么id objc_retainAutoReleasedReturnValue(id value)会去坚持该函数的方法或者函数调用方法的执行命令列表.如果有id objc_retainAutoReleasedReturnValue(id value)方法,那么对象就直接返回给方法或者函数的调用方,达到了对象不注册到autoreleasepool,也拿到了相应的对象
__weak示例声明一个__weak对象1
2
3
{
id __weak obj = strongObj;
}
LLVM编译器转换为
1
id __attribute__((objc_ownership (none ))) obj1 = strongObj
简单的调用
1
2
3
id obj
objc_initWeak(&obj ,strongObj)
objc_destoryWeak(&obj )
此时我们去文档查看objc_initWeak(8.7)
1
2
3
4
id objc_initWeak (id *object , id value ) {
*object = nil;
return objc_storeWeak(object , value );
}
先将传入的obj变为nil,然后执行objc_storeWeak函数,再看objc_destoryWeak函数(8.6)1
2
3
void objc_destroyWeak (id *object ) {
objc_storeWeak(object , nil);
}
也是去调用objc_storeWeak唯一不同的是传参不同,一个是nil,一个是value,接下来不用多说,看objc_storeWeak函数(8.18)
If value is a null pointer or the object to which it points has begun deallocation, object is assigned null and unregistered as a weak object. Otherwise, object is registered as a weak object or has its registration updated to point to value. 如果value是空指针或它指向的对象已开始解除分配,则将对象指定为null并取消注册为weak对象。 否则,将对象注册为 weak对象或将其注册更新为指向值。
objc_storeWeak作用很明显了.weak表是一个Hash table,来维护weak修饰的指针变量,objc_storeWeak传入第一个变量作为Key注册到weak表中,再根据第二个参数决定是否移除.如果第二个参数为0,那么把__weak变量从weak表中删除记录,并从引用计数表中删除对应的键值记录
二. weakSelf strongSelf用法 众所周知,打破循环引用使用__weak来修饰self,例如
__weak __typeof(self)weakSelf = self,这是AFN里面的写法__weak typeof(self) weakSelf = self,这是我们平时的写法
那么 __typeof 和 typeof有什么区别呢?其实两者是一样的,只不过兼容的问题 区别详细资料
那么 strongSelf是用来干嘛的呢,相信看过AFNetworking等一些第三方源码的会发现这种strong weak dance很常见,在block里面,如果有一些延迟操作里面用到了weakSelf,很有可能在用到之前它就已经释放了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#import <Foundation/Foundation.h>
typedef void (^ExampleBlock)(void );
@interface Person : NSObject
@property (nonatomic ,copy ) ExampleBlock exampleBlock;
@property (nonatomic , copy )NSString *name;
@end
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@end
@implementation ViewController
- (void )viewDidLoad {
[super viewDidLoad];
Person *person = [[Person alloc] init];
person.name = @"hello world" ;
__weak typeof (person) weakSelf = person;
person.exampleBlock = ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC )), dispatch_get_main_queue(), ^{
NSLog (@"%@" ,weakSelf.name);
});
};
person.exampleBlock();
}
打印结果: null
在exampleBlock()这个block执行结束后,person这个实例化的对象由于没有强指针指向,出了{}作用域就释放的了;当dispatch_after这个函数再次捕获__weak修饰的person对象的时候,由于原对象已经释放,所以会打出null
这时候__strong就派上用场了,如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#import <Foundation/Foundation.h>
typedef void (^ExampleBlock)(void );
@interface Person : NSObject
@property (nonatomic ,copy ) ExampleBlock exampleBlock;
@property (nonatomic , copy )NSString *name;
@end
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@end
@implementation ViewController
- (void )viewDidLoad {
[super viewDidLoad];
Person *person = [[Person alloc] init];
person.name = @"hello world" ;
__weak typeof (person) weakSelf = person;
person.exampleBlock = ^{
__strong __typeof (person) strongSelf = weakSelf; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC )), dispatch_get_main_queue(), ^{
NSLog (@"%@" ,strongSelf.name);
});
};
person.exampleBlock();
}
> 打印结果: hello world
三. @weakify @strongify实现原理
这俩个宏是RAC避免循环引用的宏,虽然不用RAC用不到,不过我们还是可以看下它们的实现过程
@weakify
1
2
3
#def ine weakify(...) \
rac_keywordify \
metamacro_foreach_cxt (rac_weakify_,, __weak, __VA_ARGS__)
@strongify
1
2
3
4
5
6
#define strongify (...) \
rac_keywordify \
_Pragma ("clang diagnostic push" ) \
_Pragma ("clang diagnostic ignored \" -Wshadow\"" ) \
metamacro_foreach (rac_strongify_,, __VA_ARGS__) \
_Pragma ("clang diagnostic pop" )
根本毫无头绪是不是,再接着往下看rac_keywordify
1
2
3
4
5
#if DEBUG
#define rac_keywordify autoreleasepool {}
#else
#define rac_keywordify try {} @catch (...) {}
#endif
我们发现在debug模式下使用了@autoreleasepool,这是为了维持编译器的分析能力;而使用@try/@catch是为了防止插入一些不必要的autoreleasepool;所以基本可以认为rac_keywordify时间上就是autoreleasepool{}的宏替换,再加上前面的@,形成了@autoreleasepool{} 再看weakify的第二行
metamacro_foreach_cxt(racweakify ,, weak, VA_ARGS__)
1
2
#define metamacro_foreach_cxt (MACRO, SEP, CONTEXT, ...) \
metamacro_concat (metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)
先看metamacro_concat
1
2
3
4
metamacro_concat_(A, B)
经过套入后
1
2
#define metamacro_foreach_cxt (MACRO, SEP, CONTEXT, ...) \
metamacro_foreach_cxt ##metamacro_argcount (__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)
__VA_ARGS__是可变参数,获取...中传入的N个参数
再看 metamacro_argcount(__VA_ARGS__)
1
2
#define metamacro_argcount(...) \
metamacro_at(20 , __VA_ARGS__, 20 , 19 , 18 , 17 , 16 , 15 , 14 , 13 , 12 , 11 , 10 , 9 , 8 , 7 , 6 , 5 , 4 , 3 , 2 , 1 )
这个宏的作用是用来获取参数的个数
metamacro_at
1
2
#def ine metamacro_at(N, ...) \
metamacro_concat (metamacro_at, N) (__VA_ARGS__)
实现过程
拼接metamacro_at ## N(传入的第一个值,20)(VA_ARGS ):
1
metamacro_at20(__VA_ARGS__, 20 , 19 , 18 , 17 , 16 , 15 , 14 , 13 , 12 , 11 , 10 , 9 , 8 , 7 , 6 , 5 , 4 , 3 , 2 , 1 )
metamacro_at20它的实现:
1
#define metamacro_at20(_0 , _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, ...) metamacro_head(__VA_ARGS__ )
截取前20个数,剩下的传入metamacro_head
metamacro_head定义:
1
2
3
4
#define metamacro_head (...) \
metamacro_head_ (__VA_ARGS__, 0 )
#define metamacro_head_ (FIRST, ...) FIRST
metamacro_head的作用是返回第一个参数.例如@weakify(self):1
metamacro_at20(self,20 , 19 , 18 , 17 , 16 , 15 , 14 , 13 , 12 , 11 , 10 , 9 , 8 , 7 , 6 , 5 , 4 , 3 , 2 , 1 )
截取完后,就会变成metamacro_head_(1),返回1.
再回到最初的套入##的 metamacro_foreach_cxt:1
metamacro_foreach_cxt ##metamacro_argcount (__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)
接下来再套入:
1
metamacro_foreach_cxt##N ()
接着套入刚才的@weakify(self)的例子:
1
metamacro_foreach_cxt1 (MACRO, SEP, CONTEXT, __VA_ARGS__)
1
#define metamacro_foreach_cxt1 MACRO
此时在传入之前的三个参数1
metamacro_foreach_cxt (rac_weakify_,, __weak, __VA_ARGS__)
传入后1
metamacro_foreach_cxt1(rac_weakify_, , __weak , self ) rac_weakify_(0 ,__weak ,self )
N = 20
1
2
3
4
#def ine metamacro_foreach_cxt20(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) \
metamacro_foreach_cxt19 (MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \
SEP \
MACRO (19 , CONTEXT, _19)
类似递归,先rac_weakify_(0,__weak,_19),然后把前19个数传入 metamacro_foreach_cxt19,metamacro_foreach_cxt19会rac_weakify_(0,__weak,_18),然后把前18个数传入metamacro_foreach_cxt18 …直到 metamacro_foreach_cxt1.当N=0,不错任何操作
1
#define metamacro_foreach_cxt0(MACRO, SEP, CONTEXT)
再看rac_weakify_
1
2
#define rac_weakify_(INDEX , CONTEXT , VAR ) \
CONTEXT __typeof__(VAR ) metamacro_concat(VAR , _weak_) = (VAR );
INDEX顾名思义是第几个的意思,只是一个标记,没有实际作用.例如@weakify(self):rac_weakify_(0,__weak,self)
1
__weak __typeof__ (self ) self_weak_ = self ;
而self_weak_就是弱化之后的self,这就是@weakify(self)的实现原理;
@strongify是加了一些警告,实现原理基本上是一样的;
参考 Objective-C Automatic Reference Counting (ARC) — Clang 7 documentation 剖析@weakify、@strongify - 推酷 深入研究Block用weakSelf、strongSelf、@weakify、@strongify解决循环引用 - IOS - 伯乐在线