iOS查看屏幕幀數(shù)工具--YYFPSLabel

學(xué)習(xí) YYKit 代碼時,發(fā)現(xiàn) ibireme 在項目里加入的一個查看當(dāng)前屏幕幀數(shù)的小工具,效果如下:

挺實用,實現(xiàn)方法也很簡單,但是思路特別棒但狭。

Demo: YYFPSLabel

這里是我在學(xué)習(xí) YYKit 大牛代碼的過程中的收貨披诗,順便做個筆記

一、FPSLabel 實現(xiàn)思路:

  • CADisplayLink 默認(rèn)每秒 60次立磁;
  • CADisplayLink add 到 mainRunLoop 中呈队;
  • 使用 CADisplayLinktimestamp 屬性,在 CADisplayLink 每次 tick 時唱歧,記錄上一次的 timestamp宪摧;
  • 用 _count 記錄 CADisplayLink tick 的執(zhí)行次數(shù);
  • 計算此次 tick 時, CADisplayLink 的當(dāng)前 timestamp 和 _lastTimeStamp 的差值颅崩;
  • 如果差值大于1几于,fps = _count / delta,計算得出 FPS 數(shù)沿后;

詳見 代碼沿彭。

二、NSTimer尖滚、CADisplayLink 常見問題:

2.1 問題一: UIScrollView 在滑動時喉刘,timer 會被暫停的問題。

  • 原因:runloop mode 導(dǎo)致漆弄。iOS處理滑動時睦裳,mainloop 中UIScrollView 的 mode 是 UITrackingRunLoopMode,會優(yōu)先保證界面流暢撼唾,而 timer 默認(rèn)的 model是 NSDefaultRunLoopMode廉邑,所以會出現(xiàn)被暫停。
  • 解決辦法:將 timer 加到 NSRunLoopCommonModes 中。
    [_link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];

詳見:深入理解RunLoop 一文中關(guān)于 定時器RunLoop 的 Mode 的部分

2.2 問題二:NSTimer 對于 target 的循環(huán)引用問題:

以下代碼很常見:

    CADisplayLink *_link = [CADisplayLink displayLinkWithTarget:self selector:@selector(tick:)];

    [NSTimer timerWithTimeInterval:1.f target:self selector:@selector(tick:) userInfo:nil repeats:YES];
  • 原因:以上兩種用法鬓催,都會對 self 強引用肺素,此時 timer持有 self,self 也持有 timer宇驾,循環(huán)引用導(dǎo)致頁面 dismiss 時倍靡,雙方都無法釋放,造成循環(huán)引用课舍。此時使用 __weak 也不能有效解決:
    __weak typeof(self) weakSelf = self;
    _link = [CADisplayLink displayLinkWithTarget:weakSelf selector:@selector(tick:)];

效果如下:

可以看到 頁面 dismiss 后塌西,計時器仍然在打印

2.3 解決辦法1:在頁面退出前,或者合適的時候筝尾,手動停止 timer捡需,結(jié)束循環(huán)引用。

注意:在 dealloc 方法中是肯定不行的筹淫!由于循環(huán)引用站辉,dealloc 方法不會進。

2.4 解決辦法:2:YYFPSLabel 作者提供的 YYWeakProxy

@interface YYWeakProxy : NSProxy
@end

// 使用方式:
    _link = [CADisplayLink displayLinkWithTarget:[YYWeakProxy proxyWithTarget:self] selector:@selector(tick:)];

代碼很少损姜,有興趣可以自己看下源碼饰剥。

Tips: OC 中 NSProxy 是不繼承自 NSObject 的。

三摧阅、探索環(huán)節(jié):iOS中子線程檢測主線程

在和小伙伴分享這個小工具的時候汰蓉,潘神拋出了這樣一個問題:這里組件是在主線程繪制的label,如果主線程阻塞了還能用嗎棒卷?結(jié)果是不能顾孽。

以下是探索:

3.1、模擬主線程阻塞比规,將 link 放在子線程若厚,發(fā)現(xiàn) timer 不能啟動

    // 模擬 主線程阻塞 (不應(yīng)該模擬主線程卡死,模擬卡頓即可)
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{  
        NSLog(@"即將阻塞");
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"同步阻塞主線程");
        });
        NSLog(@"不會執(zhí)行");
    });

3.2蜒什、使用 CFRunLoopAddObserver 檢測主線程是否卡頓:

//將觀察者添加到主線程runloop的common模式下的觀察中 
CFRunLoopAddObserver(CFRunLoopGetMain(), runLoopObserver, kCFRunLoopCommonModes);

這里是能檢測到主線程是否卡頓了盹沈,但是 timer 在子線程中還是跑不起來。

參考 Starming星光社檢測iOS的APP性能的一些方法

3.3吃谣、在子線程手動創(chuàng)建一個 runloop乞封,提供給 timer。

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        _link = [CADisplayLink displayLinkWithTarget:self selector:@selector(tick:)];
        // NOTE: 子線程的runloop默認(rèn)不創(chuàng)建岗憋; 在子線程獲取 currentRunLoop 對象的時候肃晚,就會自動創(chuàng)建RunLoop
        // 這里不加到 main loop,必須創(chuàng)建一個 runloop
        NSRunLoop *runloop = [NSRunLoop currentRunLoop];
        [_link addToRunLoop:runloop forMode:NSRunLoopCommonModes];
        // 必須 timer addToRunLoop 后仔戈,再run
        [runloop run];
    });

這樣关串,就可以在子線程中使用 timer 了拧廊,但是此時只能 log,無法獲通知主線程更新UI: (這里先不在主線程更新UI了)

// 嘗試1:主線程阻塞晋修, 這里就不能獲取到主線程了
//    dispatch_async(dispatch_get_main_queue(), ^{
//        阻塞時吧碾,想通過 在主線程更新UI 來查看是不可行了
//        label_.text = text;
//    });
    
    // 嘗試2:不在主線程操作 UI ,界面會發(fā)生變化
    label_.text = text;

參考: 【iOS程序啟動與運轉(zhuǎn)】- RunLoop個人小結(jié)

以上是學(xué)習(xí) YYFPSLabel 時的收獲墓卦,和對于在子線程中檢測主線程的探索倦春。詳情可以戳代碼:YYFPSLabel

四、補充:

@beychen 同學(xué)提問:為什么__weak typeof(self) weakSelf = self; 不能解決 displayLinkWithTarget 循環(huán)引用的問題落剪?

之前給的解釋是:

普通的 VC 持有 block睁本,在 block 外 weakSelf 后再傳入,block 中持有的是 weakSelf 就能正常釋放了忠怖。但是 NSTimer 的 target 傳 weakSelf 卻不行呢堰。由于 NSTimer 是閉源的,猜測原因可能如下:

1凡泣、 NSTimer 在子線程執(zhí)行枉疼,需要線程保活鞋拟,會被加入到 RunLoop 中骂维,被 Runloop 強引用;
2严卖、 Block 和 Timer 席舍,對于 self 的持有方式不同布轿, block 是捕獲了變量哮笆,進行了值拷貝。但是 NSTimer 需要一直調(diào)用 target 的 selector汰扭,如果 target 先于 NSTimer 釋放了稠肘,NSTimer 會調(diào)不到 selector,會崩潰萝毛。所以猜測 NSTimer 的 tagrget 中项阴,對 weakSelf 又進行了類似 StrongSelf 操作,eg:

    __weak typeof(self) weakSelf = self;
    self.myBlock = ^() {
        __strong typeof(weakSelf) strongSelf = weakSelf;
        // 永遠不會主動停止的動作
    };

3笆包、 由于上述 1环揽,NSTimer 本身不會被釋放(相當(dāng)于一個單例),傳入 NSTimer 的 taget庵佣,被 Timer 加到集合中的話歉胶,即使傳 weakSelf 也不能釋放。這個也是之前 debug 一個內(nèi)存泄露時發(fā)現(xiàn)的類似現(xiàn)象巴粪。

可以參考下:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末通今,一起剝皮案震驚了整個濱河市粥谬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌辫塌,老刑警劉巖漏策,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異臼氨,居然都是意外死亡掺喻,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門一也,熙熙樓的掌柜王于貴愁眉苦臉地迎上來巢寡,“玉大人,你說我怎么就攤上這事椰苟∫衷拢” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵舆蝴,是天一觀的道長谦絮。 經(jīng)常有香客問我,道長洁仗,這世上最難降的妖魔是什么层皱? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮赠潦,結(jié)果婚禮上叫胖,老公的妹妹穿的比我還像新娘。我一直安慰自己她奥,他們只是感情好瓮增,可當(dāng)我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著哩俭,像睡著了一般绷跑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上凡资,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天砸捏,我揣著相機與錄音,去河邊找鬼隙赁。 笑死垦藏,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的伞访。 我是一名探鬼主播掂骏,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼咐扭!你這毒婦竟也來了芭挽?” 一聲冷哼從身側(cè)響起滑废,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎袜爪,沒想到半個月后蠕趁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡辛馆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年俺陋,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片昙篙。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡腊状,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出苔可,到底是詐尸還是另有隱情缴挖,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布焚辅,位于F島的核電站映屋,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏同蜻。R本人自食惡果不足惜棚点,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望湾蔓。 院中可真熱鬧瘫析,春花似錦、人聲如沸默责。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽傻丝。三九已至甘有,卻和暖如春诉儒,著一層夾襖步出監(jiān)牢的瞬間葡缰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工忱反, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留泛释,地道東北人。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓温算,卻偏偏與公主長得像怜校,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子注竿,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 把網(wǎng)上的一些結(jié)合自己面試時遇到的面試題總結(jié)了一下茄茁,以后有新的還會再加進來魂贬。 1. OC 的理解與特性 OC 作為一...
    AlaricMurray閱讀 2,562評論 0 20
  • Runloop是iOS和OSX開發(fā)中非常基礎(chǔ)的一個概念裙顽,從概念開始學(xué)習(xí)付燥。 RunLoop的概念 -般說,一個線程一...
    小貓仔閱讀 993評論 0 1
  • 1. 單例寫法 單例模式確保某一個類只有一個實例愈犹,而且自行實例化并向整個系統(tǒng)提供這個實例键科。 一般情況下, 如果一個...
    sellse閱讀 1,009評論 0 1
  • 英撿起了霞掉落的鑰匙。 我說你想干嘛啊漩怎。 英說勋颖,問不出東西,咱們干脆膽子大一點勋锤。 我張大了眼睛:“你要入室作案胺沽帷?...
    段學(xué)生閱讀 1,053評論 0 1
  • 傅聰先生多次把“赤子之心”這個清澈又深不見底的詞無比崇敬地給了蕭邦叁执。 我不知道西方樂壇會怎樣跟傅聰先生探討“赤子之...
    吳玫閱讀 365評論 0 1