iOS 被滥用的weak

看其他人的代码,发现weak出现在了几乎所有有block的地方,比如 GCD 比如,使用Masnory布局的地方,


iOS 被滥用的weak_第1张图片
weak
iOS 被滥用的weak_第2张图片
weak1.jpeg

问了几个同学,理由大都是,避免循环引用,被循环引用整怕了。
反正用了也没什么不好之类的。。。

iOS 被滥用的weak_第3张图片
屏幕快照 2017-08-04 下午10.24.21.png

打个符号断点,观察一下,这两个函数在不断的调用,函数内部在操作一个不简单的list,有兴趣可以自己阅读 runtime源码。你看,不但每次调用两个开销挺大的函数,而且还有一个list

虽说现在手机好了,但是,对自己狠点,能规范的地方就规范,app会有意想不到的收获。。

以布局Monsary 为栗子,我们看看到底要不要使用weak。。

看到QQ群里有人说,出现block 必然会copy block中引用到的东西。
暂且不管Monsary 源码实现部分,

我们使用符号断点。符号断点断在

_Block_copy
_Block_release

部分,测试代码:

[self.personsLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.mostCostLabel);
        make.right.equalTo(self.mostCostLabel);
        make.top.equalTo(self.mostCostLabel.mas_bottom).offset(6.0f);
        make.height.mas_equalTo(10.0f);
}];

通过断点可以看到,当执行 block 中的语句的时候,的确会调用 _Block_copy,但是在 block 执行完之后,断点会停在 _Block_release 这里。

所以,安全可靠,不用使用weak 的,GCD 和 系统 的动画样式如果不放心,也可以这样子测试是否释放。

后记

@implementation NSTimer (PTVSafe)

+ (id)safe_scheduledTimerWithTimeInterval:(NSTimeInterval)inTimeInterval block:(void (^)())inBlock repeats:(BOOL)inRepeats {
    void (^block)() = [inBlock copy];
    id ret = [self scheduledTimerWithTimeInterval:inTimeInterval target:self selector:@selector(ptv_jdExecuteSimpleBlock:) userInfo:block repeats:inRepeats];
    return ret;
}

+ (void)ptv_jdExecuteSimpleBlock:(NSTimer *)inTimer; {
    if ([inTimer userInfo]) {
        void (^block)() = (void (^)())[inTimer userInfo];
        block();
    }
}

@end

对于NSTimer 这样子进行扩展,完全没有必要的感觉,每次都要调用copy,全局给项目中的 _Block_copy 打符号断点,timer 这里调用的甚多。释放的也不是很及时。

反思:设计一个通用组件给别人调用的时候,要想的东西,不止使用方便这个层面!

iOS 被滥用的weak_第4张图片
符号断点

UIView 自带的动画实现探究

- (void)viewDidLoad {
    
    UIView *view = [UIView new];
    view.backgroundColor = [UIColor redColor];
    view.frame = CGRectMake(0, 30, 200, 200);
    
    [self.view addSubview:view];
    
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    
        //①
//        view.frame = CGRectMake(10, 20, 30, 40);
        
        //②
        [UIView animateWithDuration:0.5
                         animations:^{
                             view.frame = CGRectMake(10, 20, 30, 40);
                         }];
    
        
    });
 }

我们顺势探究一下动画的实现过程。

使用 ① 的方式直接修改frame 是不会有动画的。
使用 ② 的方式是有动画的。

but why?

我们知道,UIView 和 CALayer 的关系,想完成动画,肯定是在layer 层做的,于是我们打符号断点给 layer 层的

- (void)addAnimation:(CAAnimation *)anim forKey:(nullable NSString *)key;

函数,看看是否会调用,该函数。重新运行,真的调用了。

QuartzCore`-[CALayer addAnimation:forKey:]:
->  0x10c569654 <+0>:   pushq  %rbp
    0x10c569655 <+1>:   movq   %rsp, %rbp
    0x10c569658 <+4>:   pushq  %r15
    0x10c56965a <+6>:   pushq  %r14
    0x10c56965c <+8>:   pushq  %r13
    0x10c56965e <+10>:  pushq  %r12
    0x10c569660 <+12>:  pushq  %rbx
    0x10c569661 <+13>:  subq   $0x18, %rsp
    0x10c569665 <+17>:  movq   %rcx, %r14
    0x10c569668 <+20>:  movq   %rdx, %r12
    0x10c56966b <+23>:  movq   %rdi, %r15
    0x10c56966e <+26>:  movq   0xc8203(%rip), %rdi       ; (void *)0x000000010c633108: CATransition

当然,我们不看汇编函数,下来我们改造一下我们的demo, 看看究竟!!

改造后的代码如下:

@interface PDLayer : CALayer

@end

@implementation PDLayer

- (void)addAnimation:(CAAnimation *)anim forKey:(NSString *)key {
    [super addAnimation:anim
                 forKey:key];
    NSLog(@"%@ %@", anim, key);
}

@end


@interface PDView : UIView

@end

@implementation PDView

+ (Class)layerClass {
    return [PDLayer class];
}

@end
- (void)viewDidLoad {
    
    PDView *view = [PDView new];
    view.backgroundColor = [UIColor redColor];
    view.frame = CGRectMake(0, 30, 200, 200);
    
    [self.view addSubview:view];
    
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    
        //①
//        view.frame = CGRectMake(10, 20, 30, 40);
        
        //②
        [UIView animateWithDuration:0.5
                         animations:^{
                             view.frame = CGRectMake(10, 20, 30, 40);
                         }];
    
        
    });

其实什么都没变,只是把上面的UIView 替换成了 PDView. 给 PDLayer 的 addAnimation 断点。看看会发生什么。

我们po 一下输出的东东

(lldb) po anim
; fillMode = both; timingFunction = easeInEaseOut; duration = 0.5; fromValue = NSPoint: {75, 90}; keyPath = position>

(lldb) po key
position

(lldb) 

可以看到,给view 的 layer 层添加了CABasicAnimation 动画, key 的 值为 position

继续执行,断点又断在了同样的地方,输出如下

(lldb) po key
bounds.origin

(lldb) po anim
; fillMode = both; timingFunction = easeInEaseOut; duration = 0.5>

这回加的key 是 bounds.origin

继续运行

断点又断了

(lldb) po key
bounds.size

(lldb) po anim
; fillMode = both; timingFunction = easeInEaseOut; duration = 0.5>

(lldb) 

这回添加的动画key 是 bounds.size。

可见UIView 的动画过程调用的都是 CABasicAnimation 系列的动画添加给 layer.

那么问题来了,为什么上面的 1 和 2 会有不同的表现呢

我们再观察两秒

        //①
//        view.frame = CGRectMake(10, 20, 30, 40);
        
        //②
        [UIView animateWithDuration:0.5
                         animations:^{
                             view.frame = CGRectMake(10, 20, 30, 40);
                         }];

我们观察calayer 的函数,又发现了

- (nullable id)actionForKey:(NSString *)event;

难道这个函数在不同的场景返回值不一样?
我们测试一下。

代码修改如下:

 //①
//        view.frame = CGRectMake(10, 20, 30, 40);
        id cc = [view.layer actionForKey:@"position"];
        NSLog(@"%@",cc);
        //②
        [UIView animateWithDuration:0.5
                         animations:^{
                             id cc = [view.layer actionForKey:@"position"];
                             NSLog(@"%@",cc);
                             
                             view.frame = CGRectMake(10, 20, 30, 40);
                         }];
    

我们观察两次 cc 有何不同

还真被蒙对了

(lldb) po cc
 nil
(lldb) c
Process 13382 resuming
2017-09-07 14:48:32.908 imageName[13382:1401757] (null)
(lldb) po cc
<_UIViewAdditiveAnimationAction: 0x60000003e660>

(lldb) 

在动画block里面打印出的值是完全不一样的。第一次是nil 第二次是个动画

我们再观察一次

[UIView performWithoutAnimation:^{
        id cc = [view.layer actionForKey:@"position"];
        NSLog(@"%@",cc);
    }];
    

这次打印出来依旧是 nil.由此,结论十分明确了。

由此,我们明白了,UIView 自带的系统动画是如何实现的,而且知道了,这种情况下,是绝对不用weak的。

你可能感兴趣的:(iOS 被滥用的weak)