以下是對我們這整段關于 Objective-C 中 Block、__block
修飾符、內存管理行為、生命周期等內容的全面總結,并附帶了一套適合面試準備的面試題集(帶答案)。
🧠 一、知識總結:Objective-C Block + __block 修飾符
? Block 的三種類型
Block 類型 | 類名(runtime) | 存儲位置 | 是否捕獲變量 | 生命周期 |
---|---|---|---|---|
全局 Block | __NSGlobalBlock__ | 全局區(.data) | ? 不捕獲 | 程序整個生命周期 |
棧 Block | __NSStackBlock__ | 棧上 | ? 捕獲 | 隨函數作用域結束銷毀 |
堆 Block | __NSMallocBlock__ | 堆上(copy 后) | ? 捕獲 | ARC/MRC 管理 |
? ARC 與 MRC 的區別
特性 | ARC | MRC |
---|---|---|
是否自動 copy Block | ? 是(捕獲變量會自動 copy 到堆) | ? 否(需手動 copy) |
NSAutoreleasePool 可用? | ? 不可用,改用 @autoreleasepool | ? 可以使用 |
retain/release 是否可用? | ? 禁止 | ? 手動管理 |
? __block
修飾符
特性 | 默認變量 | __block 修飾 |
---|---|---|
是否可在 Block 中修改 | ? 否(值捕獲) | ? 是(引用捕獲) |
捕獲方式 | 值拷貝(by value) | 引用封裝為結構體 |
生命周期 | Block 生命周期內有效 | Block 生命周期或外部引用共同決定 |
釋放機制 | 不需要釋放(副本) | 自動隨 Block 釋放;ARC 下自動 retain/release |
? 修改變量是否需要 __block
操作類型 | 是否需要 __block |
---|---|
修改對象內容(如 addObject: ) | ? 不需要 |
修改指針變量本身(如 obj = nil ) | ? 需要 |
修改基本類型變量 | ? 需要 |
捕獲但不修改變量 | ? 不需要 |
? 生命周期補充
捕獲變量類型 | 生命周期默認與 Block 相同? | 可否更長? |
---|---|---|
基本類型 | ? 是 | ? 否 |
對象類型(未被強引用) | ? 是 | ? 可被外部引用延長 |
__block 捕獲對象 | ? 是 | ? 可 retain 延長或造成循環引用 |
📚 二、面試題集
以下是整理出的 15 道典型面試題(附答案),覆蓋基礎、陷阱和進階內容:
? 基礎題
Q1: Block 有哪三種類型?它們的區別是什么?
🅰?:__NSGlobalBlock__
(不捕獲變量,放在全局區)、__NSStackBlock__
(捕獲變量,棧上,臨時)、__NSMallocBlock__
(捕獲變量 + copy 后的堆 Block,生命周期更長)。
Q2: 什么情況下 Block 是 __NSGlobalBlock__
?
🅰?:當 Block 沒有捕獲任何外部變量時,它就是 __NSGlobalBlock__
,存在全局數據段,程序整個生命周期都有效。
Q3: 在 ARC 和 MRC 下 Block 的默認行為有何不同?
🅰?:
- ARC:Block 捕獲變量時自動拷貝到堆上(自動成為
__NSMallocBlock__
) - MRC:Block 默認在棧上(
__NSStackBlock__
),需手動 copy。
Q4: 為什么要將 Block 從棧 copy 到堆?
🅰?:因為棧上的 Block 隨函數調用結束而銷毀,拷貝到堆上可以安全地在異步、延遲或跨作用域中使用。
Q5: ARC 下是否需要手動調用 copy
?
🅰?:不需要,編譯器會自動為你 copy
Block 到堆上。
? 進階題
Q6: __block
關鍵字的作用是什么?
🅰?:允許 Block 修改變量本身(不是對象內部屬性),它會將變量封裝成一個結構體,以引用方式捕獲。
Q7: 為什么修改變量需要 __block
?
🅰?:因為 Block 默認對基本變量是值拷貝,不允許修改。__block
改為引用方式封裝,可被 Block 修改。
Q8: Block 中能修改數組嗎?需要 __block
嗎?
🅰?:可以修改數組內容,不需要 __block
;但如果要讓數組指針指向其他對象,則需要 __block
。
Q9: __block
修飾的對象何時釋放?
🅰?:與 Block 生命周期一致,Block 銷毀時會釋放其引用結構體;對象本身在 ARC 下自動 retain/release。
Q10: Block 會強引用捕獲的對象嗎?
🅰?:是的,Block 默認會 retain 被捕獲的對象 ? 導致循環引用風險。
? 高階陷阱題
Q11: Block 內部使用 self
會出現什么問題?如何解決?
🅰?:會強引用 self
,導致循環引用。解決方式是用 __weak
修飾 self
的弱引用。
Q12: 在 MRC 中,為什么 NSAutoreleasePool
必須與 autorelease
配合使用?
🅰?:因為 autorelease
注冊的對象會在最近的 pool 被 drain
時釋放,pool 負責釋放“延遲對象”。
Q13: 是否有辦法讓 Block 捕獲的對象比 Block 更長壽命?
🅰?:可以。如果外部對對象有強引用(如全局變量、單例、property strong),對象就會比 Block 活得更久。
Q14: Block 是否可能與 __block
變量形成 retain cycle?
🅰?:是的。例如 obj.block = ^{ NSLog(@"%@", obj); };
就形成了互相引用,導致內存泄漏。
Q15: 如何破除 Block 的循環引用?
🅰?:使用 __weak
或 __unsafe_unretained
修飾外部對象,避免 Block 內 retain 它。