老青菜

iOS weakify strongify

2018-09-14

我们都知道block中使用 self 在某些情况下会造成retain cycle,使用__weak、__strong可以解决这个问题。大致原理如下:

  • 定义一个weakweak_self 指针,指向 self
  • block中定义一个strongself 指针,指向 weak_self

ReactiveCocoaweakifystrongify的实现也差不多,功能更多一点,可以一次标记多个变量,并且有个而特殊的@符号开头,我们一步步来看下是如何实现。

预编译代码

既然是宏,我们可以预编译后,看下源码。先写测试代码:

//先申明block属性
@interface ViewController ()
/** test block */
@property (nonatomic, copy) NSInteger (^testBlock)(void);
@end

-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    //传统的写法
//    __weak typeof(self) selfWeak = self;
//    __strong typeof(self) self = selfWeak;

    //RAC的写法
    @weakify(self);
    [self setTestBlock:^NSInteger{
        @strongify(self);
        self.view.backgroundColor = [UIColor grayColor];
        return 1;
    }];
}

执行XCode 的预编译, Xcode –> Product –> PerformAction –> Preprocess xxxx.m,找到 viewDidAppear 代码段。

-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    @autoreleasepool {} __attribute__((objc_ownership(weak))) __typeof__(self) self_weak_ = (self);;
    [self setTestBlock:^NSInteger{
        @autoreleasepool {}
# 61 "/Users/admin/git-oschina/KDIOSExample/KDExample/Page/ViewController.m"
#pragma clang diagnostic push
# 61 "/Users/admin/git-oschina/KDIOSExample/KDExample/Page/ViewController.m"
#pragma clang diagnostic ignored "-Wshadow"
# 61 "/Users/admin/git-oschina/KDIOSExample/KDExample/Page/ViewController.m"
 __attribute__((objc_ownership(strong))) __typeof__(self) self = self_weak_;
# 61 "/Users/admin/git-oschina/KDIOSExample/KDExample/Page/ViewController.m"
#pragma clang diagnostic pop
# 61 "/Users/admin/git-oschina/KDIOSExample/KDExample/Page/ViewController.m"
;
        self.view.backgroundColor = [UIColor grayColor];
        return 1;
    }];
}

很清晰的看到,最终weakifystrongify被替换成了:

// @weakify
@autoreleasepool {} __attribute__((objc_ownership(weak))) __typeof__(self) self_weak_ = (self);

// @strongify
@autoreleasepool {} __attribute__((objc_ownership(strong))) __typeof__(self) self = self_weak_;

其中 attribute((objc_ownership(weak)))attribute((objc_ownership(strong))) 分别是 __weak__strong 的实现部分,也就是等价于:

// @weakify
@autoreleasepool {} __weak __typeof__(self) self_weak_ = (self);

// @strongify
@autoreleasepool {} __strong __typeof__(self) self = self_weak_;

宏定义


我们来看一下宏的内部实现:

//weakify 定义如下:
#define weakify(...) \
    rac_keywordify \
    metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__)

这里有两个比较有意思的宏,我们一一来介绍:

rac_keywordify

#if DEBUG
#define rac_keywordify autoreleasepool {}
#else
#define rac_keywordify try {} @catch (...) {}
#endif

Debug 和 Release模式下,预编译情况如下:

//Debug:
#define weakify(...) \
    autoreleasepool {} \
    metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__)

//Release:
#define weakify(...) \
    try {} @catch (...) {} \
    metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__)

从上面我们得知:

1. @ 符号的作用,为了补全 autoreleasepool、try catch
2. rac_keywordify 区分了 Debug、Release,实现不一样

仔细看 rac_keywordify 的注释:

// Details about the choice of backing keyword:
//
// The use of @try/@catch/@finally can cause the compiler to suppress
// return-type warnings.
// The use of @autoreleasepool {} is not optimized away by the compiler,
// resulting in superfluous creation of autorelease pools.
//
// Since neither option is perfect, and with no other alternatives, the
// compromise is to use @autorelease in DEBUG builds to maintain compiler
// analysis, and to use @try/@catch otherwise to avoid insertion of unnecessary
// autorelease pools.

大致的意思是这样:
1.@try {} @catch 会导致编译器出问题,比如说Block需要返回值,但是没有写返回值,这种情况的警告会被忽略。经过测试,我把rac_keywordify的实现改成try catch后,把block里的return 1;去掉后,运行起来并没有警告。

2.@autoreleasepool 不会影响编译器的警告提示。但是编译器不会优化空的 @autoreleasepool,也就是会造成资源浪费。而空的 @try catch 会被编译器优化。所以作者最后也说了这两种方案都不是最好的方案。

metamacro_foreach_cxt


这是一个递归宏,展开之后又出现了宏metamacro_concatmetamacro_argcount,定义如下:

#define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...) \
        metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)

#define metamacro_concat(A, B) \
        metamacro_concat_(A, B)

#define metamacro_concat_(A, B) A ## B

#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)

#define metamacro_at(N, ...) \
        metamacro_concat(metamacro_at, N)(__VA_ARGS__)

#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__)

#define metamacro_head(...) \
        metamacro_head_(__VA_ARGS__, 0)

#define metamacro_head_(FIRST, ...) FIRST

metamacro_concat

连接宏,metamacro_concat(n, 1) 等价于 n1

metamacro_argcount

编译的时候获取可变参数的个数(这个有点妖,一般都是运行时获取)

就拿@weakify(self)为例,我们一步步的来看:

-> metamacro_argcount(self)
-> metamacro_at(20, self, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 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(self, 1)
-> metamacro_head_(1, 0)
-> 1

最终得到可变参数个数是1,从实现来看,可变参数个数最多支持20个,内部定义了20个宏来接受不同个数参数。

#define metamacro_at0(...) metamacro_head(__VA_ARGS__)
#define metamacro_at1(_0, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at2(_0, _1, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at3(_0, _1, _2, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at4(_0, _1, _2, _3, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at5(_0, _1, _2, _3, _4, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at6(_0, _1, _2, _3, _4, _5, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at7(_0, _1, _2, _3, _4, _5, _6, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at8(_0, _1, _2, _3, _4, _5, _6, _7, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at9(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at10(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at11(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at12(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at13(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at14(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at15(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at17(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at18(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at19(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, ...) metamacro_head(__VA_ARGS__)
#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__)

我们继续看

-> metamacro_foreach_cxt(rac_weakify_,, __weak, self)
-> metamacro_concat(metamacro_foreach_cxt, 1)(MACRO, SEP, CONTEXT, __VA_ARGS__)
-> metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, __VA_ARGS__)
-> metamacro_foreach_cxt1(rac_weakify_,, __weak, self)

同样的套路,最多支持20个参数,内部还是定义了20个宏嵌套调用。

#define metamacro_foreach_cxt0(MACRO, SEP, CONTEXT)
#define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0)
...
#define 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)

继续 metamacro_foreach_cxt1,我们接着转换:

-> metamacro_foreach_cxt1(rac_weakify_,, __weak, self)
-> rac_weakify_(0, __weak, self)

rac_weakify_ 又是怎么定义的呢

#define rac_weakify_(INDEX, CONTEXT, VAR) \
    CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR);

接着转换:

-> rac_weakify_(0, __weak, self)
-> __weak __typeof__(self) metamacro_concat(self, _weak_) = (self);
-> __weak __typeof__(self) self_weak_ = (self);

终于得到了最终的结果,可XCode预编译结果一样。

参考链接


iOS_Latte - RAC中宏的解析

Tags: objc
使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

扫描二维码,分享此文章