iOS 被濫用的weak

看其他人的代碼,發(fā)現(xiàn)weak出現(xiàn)在了幾乎所有有block的地方,比如 GCD 比如,使用Masnory布局的地方,


weak
weak1.jpeg

問了幾個(gè)同學(xué),理由大都是,避免循環(huán)引用,被循環(huán)引用整怕了。
反正用了也沒什么不好之類的。。。

屏幕快照 2017-08-04 下午10.24.21.png

打個(gè)符號斷點(diǎn),觀察一下,這兩個(gè)函數(shù)在不斷的調(diào)用,函數(shù)內(nèi)部在操作一個(gè)不簡單的list,有興趣可以自己閱讀 runtime源碼。你看,不但每次調(diào)用兩個(gè)開銷挺大的函數(shù),而且還有一個(gè)list

雖說現(xiàn)在手機(jī)好了,但是,對自己狠點(diǎn),能規(guī)范的地方就規(guī)范,app會有意想不到的收獲。。

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

看到QQ群里有人說,出現(xiàn)block 必然會copy block中引用到的東西。
暫且不管Monsary 源碼實(shí)現(xiàn)部分,

我們使用符號斷點(diǎn)。符號斷點(diǎn)斷在

_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);
}];

通過斷點(diǎn)可以看到,當(dāng)執(zhí)行 block 中的語句的時(shí)候,的確會調(diào)用 _Block_copy,但是在 block 執(zhí)行完之后,斷點(diǎn)會停在 _Block_release 這里。

所以,安全可靠,不用使用weak 的,GCD 和 系統(tǒng) 的動畫樣式如果不放心,也可以這樣子測試是否釋放。

后記

@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 這樣子進(jìn)行擴(kuò)展,完全沒有必要的感覺,每次都要調(diào)用copy,全局給項(xiàng)目中的 _Block_copy 打符號斷點(diǎn),timer 這里調(diào)用的甚多。釋放的也不是很及時(shí)。

反思:設(shè)計(jì)一個(gè)通用組件給別人調(diào)用的時(shí)候,要想的東西,不止使用方便這個(gè)層面!

符號斷點(diǎn)

UIView 自帶的動畫實(shí)現(xiàn)探究

- (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);
                         }];
    
        
    });
 }

我們順勢探究一下動畫的實(shí)現(xiàn)過程。

使用 ① 的方式直接修改frame 是不會有動畫的。
使用 ② 的方式是有動畫的。

but why?

我們知道,UIView 和 CALayer 的關(guān)系,想完成動畫,肯定是在layer 層做的,于是我們打符號斷點(diǎn)給 layer 層的

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

函數(shù),看看是否會調(diào)用,該函數(shù)。重新運(yùn)行,真的調(diào)用了。

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

當(dāng)然,我們不看匯編函數(shù),下來我們改造一下我們的demo, 看看究竟?。?/p>

改造后的代碼如下:

@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);
                         }];
    
        
    });

其實(shí)什么都沒變,只是把上面的UIView 替換成了 PDView. 給 PDLayer 的 addAnimation 斷點(diǎn)。看看會發(fā)生什么。

我們po 一下輸出的東東

(lldb) po anim
<CABasicAnimation:0x60800003f3c0; toValue = NSPoint: {0, 0}; additive = 1; delegate = <UIViewAnimationState: 0x7ff8d9d02ec0>; fillMode = both; timingFunction = easeInEaseOut; duration = 0.5; fromValue = NSPoint: {75, 90}; keyPath = position>

(lldb) po key
position

(lldb) 

可以看到,給view 的 layer 層添加了CABasicAnimation 動畫, key 的 值為 position

繼續(xù)執(zhí)行,斷點(diǎn)又?jǐn)嘣诹送瑯拥牡胤?,輸出如?/p>

(lldb) po key
bounds.origin

(lldb) po anim
<CABasicAnimation:0x600000039e60; toValue = NSPoint: {0, 0}; additive = 1; fromValue = NSPoint: {0, 0}; keyPath = bounds.origin; delegate = <UIViewAnimationState: 0x7ff8d9d02ec0>; fillMode = both; timingFunction = easeInEaseOut; duration = 0.5>

這回加的key 是 bounds.origin

繼續(xù)運(yùn)行

斷點(diǎn)又?jǐn)嗔?/p>

(lldb) po key
bounds.size

(lldb) po anim
<CABasicAnimation:0x60000003a1c0; toValue = NSSize: {0, 0}; additive = 1; fromValue = NSSize: {170, 160}; keyPath = bounds.size; delegate = <UIViewAnimationState: 0x7ff8d9d02ec0>; fillMode = both; timingFunction = easeInEaseOut; duration = 0.5>

(lldb) 

這回添加的動畫key 是 bounds.size。

可見UIView 的動畫過程調(diào)用的都是 CABasicAnimation 系列的動畫添加給 layer.

那么問題來了,為什么上面的 1 和 2 會有不同的表現(xiàn)呢

我們再觀察兩秒

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

我們觀察calayer 的函數(shù),又發(fā)現(xiàn)了

- (nullable id<CAAction>)actionForKey:(NSString *)event;

難道這個(gè)函數(shù)在不同的場景返回值不一樣?
我們測試一下。

代碼修改如下:

 //①
//        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 第二次是個(gè)動畫

我們再觀察一次

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

這次打印出來依舊是 nil.由此,結(jié)論十分明確了。

由此,我們明白了,UIView 自帶的系統(tǒng)動畫是如何實(shí)現(xiàn)的,而且知道了,這種情況下,是絕對不用weak的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • *面試心聲:其實(shí)這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,622評論 30 472
  • 在iOS中隨處都可以看到絢麗的動畫效果,實(shí)現(xiàn)這些動畫的過程并不復(fù)雜,今天將帶大家一窺ios動畫全貌。在這里你可以看...
    每天刷兩次牙閱讀 8,693評論 6 30
  • 在iOS實(shí)際開發(fā)中常用的動畫無非是以下四種:UIView動畫,核心動畫,幀動畫,自定義轉(zhuǎn)場動畫。 1.UIView...
    請叫我周小帥閱讀 3,324評論 1 23
  • 前言 本文只要描述了iOS中的Core Animation(核心動畫:隱式動畫、顯示動畫)、貝塞爾曲線、UIVie...
    GitHubPorter閱讀 3,741評論 7 11
  • 1,NSObject中description屬性的意義,它可以重寫嗎?答案:每當(dāng) NSLog(@"")函數(shù)中出現(xiàn) ...
    eightzg閱讀 4,340評論 2 19

友情鏈接更多精彩內(nèi)容