看其他人的代碼,發(fā)現(xiàn)weak出現(xiàn)在了幾乎所有有block的地方振湾,比如 GCD 比如杀迹,使用Masnory布局的地方,
問了幾個同學(xué)押搪,理由大都是树酪,避免循環(huán)引用,被循環(huán)引用整怕了大州。
反正用了也沒什么不好之類的续语。。厦画。
打個符號斷點疮茄,觀察一下滥朱,這兩個函數(shù)在不斷的調(diào)用,函數(shù)內(nèi)部在操作一個不簡單的list,有興趣可以自己閱讀 runtime源碼力试。你看徙邻,不但每次調(diào)用兩個開銷挺大的函數(shù),而且還有一個list
雖說現(xiàn)在手機(jī)好了畸裳,但是缰犁,對自己狠點,能規(guī)范的地方就規(guī)范怖糊,app會有意想不到的收獲帅容。。
以布局Monsary 為栗子伍伤,我們看看到底要不要使用weak并徘。。
看到QQ群里有人說嚷缭,出現(xiàn)block 必然會copy block中引用到的東西饮亏。
暫且不管Monsary 源碼實現(xià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);
}];
通過斷點可以看到,當(dāng)執(zhí)行 block 中的語句的時候付翁,的確會調(diào)用 _Block_copy简肴,但是在 block 執(zhí)行完之后,斷點會停在 _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,全局給項目中的 _Block_copy 打符號斷點,timer 這里調(diào)用的甚多砂竖。釋放的也不是很及時真椿。
反思:設(shè)計一個通用組件給別人調(diào)用的時候,要想的東西乎澄,不止使用方便這個層面突硝!
UIView 自帶的動畫實現(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);
}];
});
}
我們順勢探究一下動畫的實現(xiàn)過程。
使用 ① 的方式直接修改frame 是不會有動畫的置济。
使用 ② 的方式是有動畫的解恰。
but why?
我們知道锋八,UIView 和 CALayer 的關(guān)系,想完成動畫修噪,肯定是在layer 層做的查库,于是我們打符號斷點給 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, 看看究竟3肥Α!
改造后的代碼如下:
@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 斷點剃盾。看看會發(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í)行铡羡,斷點又?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)行
斷點又?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;
難道這個函數(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 第二次是個動畫
我們再觀察一次
[UIView performWithoutAnimation:^{
id cc = [view.layer actionForKey:@"position"];
NSLog(@"%@",cc);
}];
這次打印出來依舊是 nil.由此,結(jié)論十分明確了夭委。
由此幅狮,我們明白了,UIView 自帶的系統(tǒng)動畫是如何實現(xiàn)的闰靴,而且知道了彪笼,這種情況下,是絕對不用weak的蚂且。