iOS內存管理相關

0x00 堆棧

  • 堆(heap):由程序猿負責分配和管理,存儲OC對象,比方說繼承自NSObject的全部對象,這些對象都是引用類型。
  • 棧(stack):由系統負責管理。存儲如int, float等數據類型,這些對象都是值類型。棧的效率會相對較高。

0x01 對象分配和釋放

內存管理方式

圖片來源https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html

Apple提供了兩種內存管理方式:html

  1. MRR(Manual retain-release), 手動保留釋放。一般在運行時環境中使用。
  2. ARC(Automatic Reference Counting), 自動引用計數。在編譯時使用。使用ARC的話,至關於將內存的管理下放到系統,咱們只需關心對象的實現,而無需關心對象的釋放與否的問題。(基本狀況下不用care)。

內存管理原則

  1. 你持有本身所擁有的對象:經過使用alloc, new, copy或者mutableCopy等方法命名的方法建立的對象,像stringWithFormat:等方法建立的對象並不能被持有
  2. 使用retain來獲取對象的擁有關係:retain使用場景有兩個: 1)在訪問器的方法實現或者是init方法中,獲取想要的對象,並將其做爲屬性值 2)避免其餘操做對該對象的形成的影響
  3. 在不使用該對象以後,必須捨棄掉該對象:發送release或者autorelease的消息給該對象
  4. 不能釋放非本身持有的對象!

releaseautorelease

發送release消息的話,會立刻將對象釋放掉。若是想要延遲釋放對象的話,可使用autorelease.git

經過引用返回的對象並不能被持有。github

使用dealloc來捨棄對象的持有

這裏須要注意的一點是不能直接調用其餘對象的dealloc方法app

不要再init或者dealloc方法中使用訪問器方法

蘋果在官方文檔中提到,不要在初始化或者是釋放方法中使用Accessor Methods(這多是在MRC階段的,蘋果的內存管理文檔是比較久以前,不過這是我的理解,有待驗證)。因此在初始化的方法中,應該這樣:ide

- (instancetype)init {
  self = [super init];
  if (self) {
    _count = [[NSNumber alloc] initWithInteger: 0];
  }
  return self;
}
複製代碼

Core Foundation

對於通常的OC對象,只須要使用ARC來進行計數實現內存管理便可。對於底層的Core Foundation來講,則須要調用CFRetain()CFRelease()這兩個方法來增減引用計數。工具

OC對象和Core Foundation對象的轉換經過bridge關鍵字進行轉換。oop

  • __bridge: 只進行類型轉換,不修改引用計數
  • __bridge_retained: 類型轉換後引用計數加1
  • __bridge_transfer: 類型轉換後將對象的引用計數交給ARC管理

weak

weak引用是一種non-owning的關係,因此並不會retain對象。ui

聲明爲weak的變量能夠防止循環引用形成的內存問題,由於若是對象沒有了強引用的話,就會被置爲nil。這一點須要跟unsafe_unretained區分開來。unsafe_unretained的對象若是沒有了強引用,並不會被置爲nil。若是對象被回收或者銷燬的話,該指針就會變爲野指針。spa

retain一個對象會建立該對象的強引用。命令行

循環引用的解決

圖片來源:Advanced Memory Management Programming Guide

循環引用的一種解決方式是使用weak引用。如圖中所示,Cocoa有一個約定,就是父對象對其子對象是強引用,子對象則是對父對象弱引用。用上面的例子來講的話,就是Page對象強引用了Paragraph, Paragraph弱引用了Page.

發送消息給已經釋放的對象的話,程序會crash,這點須要跟發送消息給nil進行區分。OC是容許發送消息給nil的。

集合類型retain其包含的對象

如Array, Dictionary和Set等集合對象,retain其object,在移除對象或者集合對象被釋放的時候纔會將該object捨棄。

0x02 野指針錯誤

野指針致使的錯誤一般在Xcode中表現爲:Thread 1: EXC_BAD_ACCESS(code=EXC_I386_GPFIT)錯誤。緣由是訪問了一塊已經釋放或者不屬於你的內存。

0x03 Retained/Unretained return value

  • Retained return value: 表示調用者擁有這個返回值,須要負責釋放。比方說alloc, copy, init, mutableCopynew打頭的方法
  • Unretained return value: 調用者不擁有這個返回值。如[NSString stringWithFormat:]這樣的方法

0x04 內存泄露

內存泄露有兩種類型:

  • Leaked memory: Memory unreferenced by your application that cannot be used again or freed (also detectable by using the Leaks instrument).
  • Abandoned memory: Memory still referenced by your application that has no useful purpose.

其中第一種可使用Leak工具來檢測,第二種使用Allocations來檢測

0x05 Block的分配與釋放

Block一開始是釋放在棧中,可是在運行時會調用Block_copy方法將block對象複製到堆中,而且將引用計數加1,返回新的block指針(若是block對象已經存在於堆中的話,則引用計數加1)。而Block_release方法則是將引用計數減一,或者在計數爲0時將對象釋放摧毀。

#0x08 Autorelease Pool Block

@autoreleasepool {
  // Code
}
複製代碼

通常在下面三種狀況下須要使用autoreleasepool

  1. 比方說像命令行工具等不基於UI framework的程序
  2. 在本身建立的loop中建立了不少臨時對象
  3. 生成輔助線程:這種狀況的話必須在線程執行時建立本身的autoreleasepool block, 不然會形成內存泄漏

0x07 Other

  1. 通常狀況下,allocrelease配套使用

參考文章傳送門

  1. Objective-C 中的內存分配
相關文章
相關標籤/搜索