首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

Objective-C 記憶體管理核心:循環引用成因、檢測與解決方案(弱引用/所有權修飾符)

     編輯:關於C

背景:手動引用計數 (MRC) 時代的痛點

雖然現代 iOS 使用 ARC,但理解循環引用本質對調試洩漏、處理 Core Foundation/跨語言橋接仍至關重要。

實驗:Dog ↔ Person 相互引用

類定義

// Dog.h
@interface Dog : NSObject
@property (nonatomic, assign) Person *owner;  // assign 不增加引用計數
- (void)dealloc;
@end

// Person.h
@interface Person : NSObject
@property (nonatomic, retain) Dog *dog;       // retain 增加引用計數
- (void)dealloc;
@end

實現與測試

// Dog.m
@implementation Dog
- (void)dealloc { NSLog(@"Dog dealloc"); [super dealloc]; }
@end

// Person.m
@implementation Person
- (void)dealloc { [_dog release]; NSLog(@"Person dealloc"); [super dealloc]; }
@end

// main.m
int main() {
    Person *p = [[Person alloc] init];  // retainCount = 1
    Dog *d = [[Dog alloc] init];        // retainCount = 1

    p.dog = d;  // d retainCount = 2
    d.owner = p; // p retainCount = 2 (assign 不增)

    [p release];  // p = 1
    [d release];  // d = 1

    // 此時雙方 retainCount 均為 1,dealloc 未調用 → 洩漏
    NSLog(@"End");
    return 0;
}

現象分析

  • p.dog = d → d retainCount +1
  • d.owner = p (assign) → p retainCount 不變
  • 釋放後:p=1, d=1,雙方仍持有對方,**引用計數永不歸零**
  • dealloc 不執行 → [_dog release] 不執行 → 死鎖

解決方案:單向弱引用

// Dog.h 修正
@property (nonatomic, assign) Person *owner;  // assign / weak 皆可打破環
// 或 ARC 寫法
@property (nonatomic, weak) Person *owner;

原則:父→子 strong/retain,子→父 weak/assign。

ARC 時代的循環引用與 @autoreleasepool

// ARC 下同樣會洩漏
@property (nonatomic, strong) Dog *dog;
@property (nonatomic, strong) Person *owner;  // 雙向 strong → 環

// 修正
@property (nonatomic, weak) Person *owner;

ARC 編譯器會對 weak 自動置零,對象釋放時自動斷開引用。

檢測工具與最佳實踐

工具/方法用途
Instruments → Leaks運行時自動檢測循環引用洩漏
Debug Memory Graph (Xcode 8+)可視化對象引用圖,高亮循環
Analyze (Shift+Cmd+B)靜態分析潛在洩漏
命名規範delegate、block 回調、父指針統一用 weak

Block 循環引用專項

__weak typeof(self) weakSelf = self;
self.completion = ^{ [weakSelf handleResult]; };
// 或
__block typeof(self) blockSelf = self;  // MRC 手動置 nil
self.completion = ^{ [blockSelf handleResult]; blockSelf = nil; };

總結

循環引用本質是強引用環導致引用計數永不歸零。解決核心:**識別所有權層級,父強子弱,代理/塊/回調用弱引用**。掌握 Instruments Leaks、Memory Graph 兩大檢測神器,配合命名規範與代碼審查,能從根源消除內存洩漏。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved