《招聘一個靠譜的 iOS》筆記

這里只是對《招聘一個靠譜的 iOS》個人覺得比較重要的一些筆記摘抄以便日后復習,有需要的建議看原文录粱。

26. runtime如何實現(xiàn)weak變量的自動置nil?

runtime 對注冊的類画拾, 會進行布局啥繁,對于 weak 對象會放入一個 hash 表中。 用 weak 指向的對象內存地址作為 key青抛,當此對象的引用計數(shù)為0的時候會 dealloc旗闽,假如 weak 指向的對象內存地址是a,那么就會以a為鍵蜜另, 在這個 weak 表中搜索适室,找到所有以a為鍵的 weak 對象,從而設置為 nil蚕钦。

27. 能否向編譯后得到的類中增加實例變量亭病?能否向運行時創(chuàng)建的類中添加實例變量?為什么嘶居?

  • 不能向編譯后得到的類中增加實例變量罪帖;

  • 能向運行時創(chuàng)建的類中添加實例變量促煮;
    解釋下:

  • 因為編譯后的類已經注冊在 runtime 中,類結構體中的 objc_ivar_list 實例變量的鏈表 和 instance_size 實例變量的內存大小已經確定整袁,同時runtime 會調用 class_setIvarLayout 或 class_setWeakIvarLayout 來處理 strong weak 引用菠齿。所以不能向存在的類中添加實例變量;

  • 運行時創(chuàng)建的類是可以添加實例變量坐昙,調用 class_addIvar 函數(shù)绳匀。但是得在調用 objc_allocateClassPair 之后,objc_registerClassPair 之前炸客,原因同上疾棵。

31. 猜想runloop內部是如何實現(xiàn)的?

一般來講痹仙,一個線程一次只能執(zhí)行一個任務是尔,執(zhí)行完成后線程就會退出。如果我們需要一個機制开仰,讓線程能隨時處理事件但并不退出拟枚,通常的代碼邏輯 是這樣的:

function loop() {
    initialize();
    do {
        var message = get_next_message();
        process_message(message);
    } while (message != quit);
}

或使用偽代碼來展示下:

// 
// http://weibo.com/luohanchenyilong/ (微博@iOS程序犭袁)
// https://github.com/ChenYilong
int main(int argc, char * argv[]) {
 //程序一直運行狀態(tài)
 while (AppIsRunning) {
      //睡眠狀態(tài),等待喚醒事件
      id whoWakesMe = SleepForWakingUp();
      //得到喚醒事件
      id event = GetEvent(whoWakesMe);
      //開始處理事件
      HandleEvent(event);
 }
 return 0;
}

34. 不手動指定autoreleasepool的前提下众弓,一個autorealese對象在什么時刻釋放恩溅?(比如在一個vc的viewDidLoad中創(chuàng)建)

分兩種情況:手動干預釋放時機、系統(tǒng)自動去釋放谓娃。

  1. 手動干預釋放時機--指定autoreleasepool 就是所謂的:當前作用域大括號結束時釋放脚乡。
  2. 系統(tǒng)自動去釋放--不手動指定autoreleasepool

Autorelease對象出了作用域之后,會被添加到最近一次創(chuàng)建的自動釋放池中滨达,并會在當前的 runloop 迭代結束時釋放每窖。

釋放的時機總結起來,可以用下圖來表示:

autoreleasepool與 runloop 的關系圖

下面對這張圖進行詳細的解釋:

從程序啟動到加載完成是一個完整的運行循環(huán)弦悉,然后會停下來,等待用戶交互蟆炊,用戶的每一次交互都會啟動一次運行循環(huán)稽莉,來處理用戶所有的點擊事件、觸摸事件涩搓。

我們都知道: 所有 autorelease 的對象污秆,在出了作用域之后,會被自動添加到最近創(chuàng)建的自動釋放池中昧甘。

但是如果每次都放進應用程序的 main.m 中的 autoreleasepool 中良拼,遲早有被撐滿的一刻。這個過程中必定有一個釋放的動作充边。何時庸推?

在一次完整的運行循環(huán)結束之前常侦,會被銷毀。

那什么時間會創(chuàng)建自動釋放池贬媒?運行循環(huán)檢測到事件并啟動后聋亡,就會創(chuàng)建自動釋放池。

子線程的 runloop 默認是不工作际乘,無法主動創(chuàng)建坡倔,必須手動創(chuàng)建。

自定義的 NSOperation 和 NSThread 需要手動創(chuàng)建自動釋放池脖含。比如: 自定義的 NSOperation 類中的 main 方法里就必須添加自動釋放池罪塔。否則出了作用域后,自動釋放對象會因為沒有自動釋放池去處理它养葵,而造成內存泄露征堪。

但對于 blockOperation 和 invocationOperation 這種默認的Operation ,系統(tǒng)已經幫我們封裝好了港柜,不需要手動創(chuàng)建自動釋放池请契。

@autoreleasepool 當自動釋放池被銷毀或者耗盡時,會向自動釋放池中的所有對象發(fā)送 release 消息夏醉,釋放自動釋放池中的所有對象爽锥。

如果在一個vc的viewDidLoad中創(chuàng)建一個 Autorelease對象,那么該對象會在 viewDidAppear 方法執(zhí)行前就被銷毀了畔柔。

參考鏈接:《黑幕背后的Autorelease》

38. 在block內如何修改block外部變量氯夷?

默認情況下,在block中訪問的外部變量是復制過去的靶擦,即:寫操作不對原變量生效腮考。但是你可以加上 __block 來讓其寫操作生效,示例代碼如下:

__block int a = 0;
void (^foo)(void) = ^{ 
    a = 1; 
};
foo(); 
//這里玄捕,a的值被修改為1

這是 微博@唐巧_boy的《iOS開發(fā)進階》中的第11.2.3章節(jié)中的描述踩蔚。你同樣可以在面試中這樣回答,但你并沒有答到“點子上”枚粘。真正的原因馅闽,并沒有書這本書里寫的這么“神奇”,而且這種說法也有點牽強馍迄。面試官肯定會追問“為什么寫操作就生效了福也?”真正的原因是這樣的:

我們都知道:Block不允許修改外部變量的值,這里所說的外部變量的值攀圈,指的是棧中指針的內存地址暴凑。__block 所起到的作用就是只要觀察到該變量被 block 所持有,就將“外部變量”在棧中的內存地址放到了堆中赘来。進而在block內部也可以修改外部變量的值现喳。
Block不允許修改外部變量的值凯傲。Apple這樣設計,應該是考慮到了block的特殊性拿穴,block也屬于“函數(shù)”的范疇泣洞,變量進入block,實際就是已經改變了作用域默色。在幾個作用域之間進行切換時球凰,如果不加上這樣的限制,變量的可維護性將大大降低腿宰。又比如我想在block內聲明了一個與外部同名的變量呕诉,此時是允許呢還是不允許呢?只有加上了這樣的限制吃度,這樣的情景才能實現(xiàn)甩挫。于是棧區(qū)變成了紅燈區(qū),堆區(qū)變成了綠燈區(qū)椿每。

我們可以打印下內存地址來進行驗證:

   __block int a = 0;
   NSLog(@"定義前:%p", &a);         //棧區(qū)
   void (^foo)(void) = ^{
       a = 1;
       NSLog(@"block內部:%p", &a);    //堆區(qū)
   };
   NSLog(@"定義后:%p", &a);         //堆區(qū)
   foo();
2016-05-17 02:03:33.559 LeanCloudChatKit-iOS[1505:713679] 定義前:0x16fda86f8
2016-05-17 02:03:33.559 LeanCloudChatKit-iOS[1505:713679] 定義后:0x155b22fc8
2016-05-17 02:03:33.559 LeanCloudChatKit-iOS[1505:713679] block內部: 0x155b22fc8

“定義后”和“block內部”兩者的內存地址是一樣的伊者,我們都知道 block 內部的變量會被 copy 到堆區(qū),“block內部”打印的是堆地址间护,因而也就可以知道亦渗,“定義后”打印的也是堆的地址。

那么如何證明“block內部”打印的是堆地址汁尺?

把三個16進制的內存地址轉成10進制就是:

  1. 定義后前:6171559672
  2. block內部:5732708296
  3. 定義后后:5732708296

中間相差438851376個字節(jié)法精,也就是 418.5M 的空間,因為堆地址要小于棧地址痴突,又因為iOS中一個進程的棧區(qū)內存只有1M搂蜓,Mac也只有8M,顯然a已經是在堆區(qū)了辽装。

這也證實了:a 在定義前是棧區(qū)帮碰,但只要進入了 block 區(qū)域,就變成了堆區(qū)拾积。這才是 __block 關鍵字的真正作用收毫。

__block 關鍵字修飾后,int類型也從4字節(jié)變成了32字節(jié)殷勘,這是 Foundation 框架 malloc 出來的。這也同樣能證實上面的結論昔搂。(PS:居然比 NSObject alloc 出來的 16 字節(jié)要多一倍)玲销。

理解到這是因為堆棧地址的變更,而非所謂的“寫操作生效”摘符,這一點至關重要贤斜,要不然你如何解釋下面這個現(xiàn)象:

以下代碼編譯可以通過策吠,并且在block中成功將a的從Tom修改為Jerry。

NSMutableString *a = [NSMutableString stringWithString:@"Tom"];
   NSLog(@"\n 定以前:------------------------------------\n\
         a指向的堆中地址:%p瘩绒;a在棧中的指針地址:%p", a, &a);               //a在棧區(qū)
   void (^foo)(void) = ^{
       a.string = @"Jerry";
       NSLog(@"\n block內部:------------------------------------\n\
        a指向的堆中地址:%p猴抹;a在棧中的指針地址:%p", a, &a);               //a在棧區(qū)
       a = [NSMutableString stringWithString:@"William"];
   };
   foo();
   NSLog(@"\n 定以后:------------------------------------\n\
         a指向的堆中地址:%p;a在棧中的指針地址:%p", a, &a);               //a在棧區(qū)
image

這里的a已經由基本數(shù)據類型锁荔,變成了對象類型蟀给。block會對對象類型的指針進行copy,copy到堆中阳堕,但并不會改變該指針所指向的堆中的地址跋理,所以在上面的示例代碼中,block體內修改的實際是a指向的堆中的內容恬总。

但如果我們嘗試像上面圖片中的65行那樣做前普,結果會編譯不通過,那是因為此時你在修改的就不是堆中的內容壹堰,而是棧中的內容拭卿。

上文已經說過:Block不允許修改外部變量的值,這里所說的外部變量的值贱纠,指的是棧中指針的內存地址峻厚。棧區(qū)是紅燈區(qū),堆區(qū)才是綠燈區(qū)并巍。

50. 如何關閉默認的KVO的默認實現(xiàn)目木,并進入自定義的KVO實現(xiàn)?

image

添加Observer
通過runtime偷偷實現(xiàn)了一個子類懊渡,并且以NSKVONotifying_+類名來命名
將之前那個對象的isa指針指向了這個子類刽射,并重寫class方法偽裝成原類。
重寫了觀察的對象setter方法剃执,并且在重寫的中添加了willChangeValueForKey:以及didChangeValueForKey:
移除Observer
只是簡單的將對象的isa指向原來的類對象中誓禁。

請參考:

  1. 《如何自己動手實現(xiàn) KVO》
  2. KVO for manually implemented properties
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市肾档,隨后出現(xiàn)的幾起案子摹恰,更是在濱河造成了極大的恐慌,老刑警劉巖怒见,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件俗慈,死亡現(xiàn)場離奇詭異,居然都是意外死亡遣耍,警方通過查閱死者的電腦和手機闺阱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來舵变,“玉大人酣溃,你說我怎么就攤上這事瘦穆。” “怎么了赊豌?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵扛或,是天一觀的道長。 經常有香客問我碘饼,道長熙兔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任派昧,我火速辦了婚禮黔姜,結果婚禮上,老公的妹妹穿的比我還像新娘蒂萎。我一直安慰自己秆吵,他們只是感情好,可當我...
    茶點故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布五慈。 她就那樣靜靜地躺著纳寂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪泻拦。 梳的紋絲不亂的頭發(fā)上毙芜,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天,我揣著相機與錄音争拐,去河邊找鬼腋粥。 笑死,一個胖子當著我的面吹牛架曹,可吹牛的內容都是我干的隘冲。 我是一名探鬼主播,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼绑雄,長吁一口氣:“原來是場噩夢啊……” “哼展辞!你這毒婦竟也來了?” 一聲冷哼從身側響起万牺,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤罗珍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后脚粟,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體覆旱,經...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年核无,在試婚紗的時候發(fā)現(xiàn)自己被綠了扣唱。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖画舌,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情已慢,我是刑警寧澤曲聂,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站佑惠,受9級特大地震影響朋腋,放射性物質發(fā)生泄漏。R本人自食惡果不足惜膜楷,卻給世界環(huán)境...
    茶點故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一旭咽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧赌厅,春花似錦穷绵、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至揍障,卻和暖如春目养,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背毒嫡。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工癌蚁, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人兜畸。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓努释,卻偏偏與公主長得像,于是被迫代替她去往敵國和親膳叨。 傳聞我的和親對象是個殘疾皇子洽洁,可洞房花燭夜當晚...
    茶點故事閱讀 44,678評論 2 354

推薦閱讀更多精彩內容