Objective-C記憶體管理


記憶體管理是任何程式設計語言中最重要的過程之一。它是在需要時分配物件的記憶體並在不再需要時取消分配的過程。

管理物件記憶體是一個效能問題; 如果應用程式不釋放不需要的物件,則應用程式會因記憶體占用增加並且效能受損。

Objective-C記憶體管理技術大致可分為兩類 -

  • 「手動保留或釋放」或MRR
  • 「自動參考計數」或ARC

1. 「手動保留釋放」或MRR

在MRR中,通過跟蹤自己的物件來明確管理記憶體。這是使用一個稱為參照計數的模型實現的,FoundationNSObject與執行時環境一起提供。

MRR和ARC之間的唯一區別是保留和釋放,前者是手動處理,而後者則自動處理。

下圖表示記憶體管理在Objective-C中的工作方式範例。

A類物件的記憶體生命週期如上圖所示。 如您所見,保留計數顯示在物件下方,當物件的保留計數變為0時,物件將被完全釋放,並且其記憶體將被釋放以供其他物件使用。

首先使用NSObject中提供的alloc/init方法建立A類物件。 現在,保留計數變為1
現在,B類保留了A類的物件,A類物件的保留計數變為2

然後,C類拷貝該物件的副本。它被建立為A類的另一個範例,具有相同的範例變數值。 這裡,保留計數是1而不是原始物件的保留計數。如圖中的虛線表示。

使用release方法由C類釋放複製的物件,並且保留計數變為0,因此物件被銷毀。

對於初始的A類物件,保留計數為2,所以必須釋放兩次才能銷毀它。 這是通過A類和B類的釋放語句完成的,它們將保留計數分別減少到10。 最後,物件就被銷毀了。

2. MRR基本規則

  • 擁有建立的任何物件:使用名稱以「alloc」,「new」,「copy」或「mutableCopy」開頭的方法建立物件

  • 使用retain獲取物件的所有權:通常保證接收到的物件在接收到的方法中保持有效,並且該方法也可以安全地將物件返回給它的呼叫者。在兩種情況下使用retain -

    • 在存取器方法或init方法的實現中,獲取想要儲存為物件屬性值的物件的所有權。
    • 防止物件因某些其他操作的副作用而失效。
  • 當不再需要它時,必須放棄對擁有的物件的所有權:通過向物件傳送釋放訊息或自動釋放訊息來放棄物件的所有權。 因此,在Cocoa術語中,放棄物件的所有權通常被稱為「釋放」物件。

  • 不得放棄不擁有的物件的所有權。

範例程式碼

#import <Foundation/Foundation.h>

@interface SampleClass:NSObject
- (void)sampleMethod;
@end

@implementation SampleClass
- (void)sampleMethod {
   NSLog(@"Hello, World! \n");
}

- (void)dealloc  {
  NSLog(@"Object deallocated");
  [super dealloc];
}

@end

int main() {

   /* 第一個Objective-C程式 */
   SampleClass *sampleClass = [[SampleClass alloc]init];
   [sampleClass sampleMethod];

   NSLog(@"Retain Count after initial allocation: %d", 
   [sampleClass retainCount]);
   [sampleClass retain];

   NSLog(@"Retain Count after retain: %d", [sampleClass retainCount]);
   [sampleClass release];
   NSLog(@"Retain Count after release: %d", [sampleClass retainCount]);
   [sampleClass release];
   NSLog(@"SampleClass dealloc will be called before this");

   // 應該將物件設定為nil
   sampleClass = nil;
   return 0;
}

執行上面範例程式碼,得到以下結果 -

2018-11-16 07:02:42.556 main[152785] Hello, World! 
2018-11-16 07:02:42.558 main[152785] Retain Count after initial allocation: 1
2018-11-16 07:02:42.558 main[152785] Retain Count after retain: 2
2018-11-16 07:02:42.558 main[152785] Retain Count after release: 1
2018-11-16 07:02:42.558 main[152785] Object deallocated
2018-11-16 07:02:42.558 main[152785] SampleClass dealloc will be called before this

3. 「自動參考計數」或ARC

在自動參照計數或ARC中,系統使用與MRR相同的參照計數系統,但它在編譯時為我們插入適當的記憶體管理方法呼叫。 強烈建議將ARC用於新專案。 如果使用ARC,通常不需要理解本文件中描述的底層實現,儘管在某些情況下它可能會有所幫助。 有關ARC的更多資訊,請參閱ARC發行說明

如上所述,在ARC中,不需要新增releaseretain方法,因為編譯器會對此進行處理。 實際上,Objective-C的基本過程仍然是相同的。 它在內部使用保留和釋放操作,使開發人員更容易編碼而無需擔心這些操作,這將減少寫入的程式碼量和記憶體洩漏的可能性。

還有另一個原則叫做垃圾收集,它在Mac OS-X中與MRR一起使用,但由於它在OS-X Mountain Lion中的棄用,它還沒有與MRR一起討論過。 此外,iOS物件從未擁有垃圾收集功能。 使用ARC,OS-X中也沒有使用垃圾收集。

這是一個簡單的ARC範例。 請注意,這不適用於線上編譯器,因為它不支援ARC。

#import <Foundation/Foundation.h>

@interface SampleClass:NSObject
- (void)sampleMethod;
@end

@implementation SampleClass
- (void)sampleMethod {
   NSLog(@"Hello, World! \n");
}

- (void)dealloc  {
  NSLog(@"Object deallocated");
}

@end

int main() {
   /* my first program in Objective-C */
   @autoreleasepool {
      SampleClass *sampleClass = [[SampleClass alloc]init];
      [sampleClass sampleMethod];
      sampleClass = nil;
   }
   return 0;
}

執行上面範例程式碼,得到以下結果 -

2018-10-28 08:45:17.210 demo[8385] Hello, World!
2018-10-28 08:45:17.211 demo[8385] Object deallocated