循环引用由来 俩个对象相互强引用,俩个对象的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 - 伯乐在线