Objective-C總結(jié)----5.內(nèi)存管理

可執(zhí)行程序構(gòu)成

  • Objective-C可執(zhí)行程序是由(可執(zhí)行)代碼、初始化和未初始化的程序數(shù)據(jù)、鏈接信息、重定位信息、局部數(shù)據(jù)和動態(tài)數(shù)據(jù)構(gòu)成的。
    • 可執(zhí)行程序、程序數(shù)據(jù)以及鏈接在重定位信息會以靜態(tài)方式分配內(nèi)存,并在程序的生命周期一直存在。
    • 局部數(shù)據(jù)在語句塊中聲明并且僅在該語句塊中有效,當該語句塊執(zhí)行后局部數(shù)據(jù)不在繼續(xù)存在,存在棧內(nèi)存中,系統(tǒng)自動管理。
    • "動態(tài)數(shù)據(jù)"->Objective-C將創(chuàng)建的對象存儲在動態(tài)分配的內(nèi)存即“堆”內(nèi)存中,我們需要進行唯一的內(nèi)存管理。
  • 程序數(shù)據(jù)包包括以靜態(tài)方式聲明的變量和程序變量(即在程序編譯時在代碼中設(shè)置的常數(shù))。

內(nèi)存管理不當引起的問題

  • 內(nèi)存泄漏
    如果程序沒有釋放不再使用的對象,就會導(dǎo)致內(nèi)存泄漏,造成內(nèi)存浪費,容易耗盡系統(tǒng)內(nèi)存。
  • 懸掛指針
    如果程序釋放了仍在使用的對象,就會導(dǎo)致該問題,為野指針或僵尸指針,向懸掛指針發(fā)送消息會導(dǎo)致程序奔潰。

Objective-C內(nèi)存管理方式

Objective-C內(nèi)存管理是通過引用計數(shù)實現(xiàn)的,引用計數(shù)是一種通過對象的唯一引用,確定對象是否正在被使用。如果對象的引用計數(shù)為零時,對象就被視為不再使用,系統(tǒng)將釋放這個對象的內(nèi)存。

蘋果公司提供兩種方式管理內(nèi)存:

  • 手動管理MRR/MRC
  • 自動管理ARC->蘋果推薦使用ARC,已經(jīng)很成熟的計數(shù)

1.手動管理

手動管理是建立在“對象所有權(quán)”概念上的內(nèi)存管理機制,只要對象的所有者還存在,對象就不會被Objective-C運行時環(huán)境釋放。

  • 對象引用&對象所有權(quán)
    Objective-C對象是通過指向Objective-C對象內(nèi)存地址的變量,以間接方式訪問,這種變量也稱為指針。
    如 KNZPerson *person = [[KNZPerson alloc]init];
    • person為一個指針變量,保存著對象的內(nèi)存地址
    • [[KNZPerson alloc]init]為創(chuàng)建一個對象并初始化,返回對象的地址</br>

再創(chuàng)建一個指針變量person2,將person賦值給person2,那么person2擁有指向該對象的內(nèi)存地址。

 -  KNZPerson *person2  = person;

如果person2沒有獲得該對象的所有權(quán)(通過賦值語句無法獲得所有權(quán))的話,當person被釋放(person不再指向該對象,該對象被釋放),那么person2保存的對象地址指向的內(nèi)存空間不再存在,person2就是懸掛指針,因此我們需要以手動的方式管理對象的生命周期(保留和釋放),在編寫代碼時***(面向類Class設(shè)計時添加內(nèi)存管理代碼)***必須遵守一些內(nèi)存管理規(guī)則。
  • 手動內(nèi)存管理規(guī)則
    • 為創(chuàng)建的所有對象設(shè)置所有權(quán)
      • 創(chuàng)建:alloc、new、copy或者mutableCopy,計數(shù)器+1

      注意:Foundation里面的類用alloc創(chuàng)建時也需要進行內(nèi)存管理,但是使用一些便利方法創(chuàng)建的不需要:
      例如:NSString *str = [[NSString alloc]initWithFormat:@"A1"];
      需要[str release];
      NSString *str2 = [NSString stringWithFarmat:@"A2"];
      不需要進行內(nèi)存管理
      - 應(yīng)使用retain方法獲取對象的所有權(quán)
      - retain:計數(shù)器+1
      - 當不再使用某個對象時,必須放棄其所有權(quán)
      - release:計數(shù)器-1
      - 當計數(shù)器=0對象自動釋放,釋放時自動調(diào)用dealloc方法
      - 不能放棄不歸你所有的對象所有權(quán)

例如:
KNZPerson *person = [[KNZPerson alloc]init];
alloc創(chuàng)建一個對象并且賦予person,計數(shù)器+1,當前值為1;
KNZPerson *person2 = [person retain];
person指向的對象發(fā)送消息retain,計數(shù)器+1,當前值為2;
當person不再指向該對象的話[person release];
計數(shù)器-1,當前值為1;
還有指針變量person2指向該對象,該對象不會被釋放;
[person2 release];person2也不再指向該對象,計數(shù)器-1,當前值為0,
系統(tǒng)認為該對象不再被使用,系統(tǒng)自動調(diào)用dealloc方法釋放掉該對象。

MRR管理內(nèi)存情況下如何設(shè)計一個類
  • 設(shè)計一個類擁有其他對象時,初始化時被創(chuàng)建的其他對象引用計數(shù)器+1(記得在對象釋放調(diào)用dealloc方法中,釋放掉這些其他對象)
  • 創(chuàng)建這個類的實例對象時,這個對象的引用計數(shù)器自動+1,就可以擁有這個對象了,不用管對象內(nèi)部所擁有的其他對象。
  • 當這個對象的引用計數(shù)器=0時,系統(tǒng)自動調(diào)用對象的dealloc方法釋放此對象,當對象擁有其他對象時,需要在dealloc方法里面釋放掉所擁有的對象,最后調(diào)用[super dealloc]方法,徹底釋放對象。
@autorelease{自動釋放代碼塊}
  • 在自動釋放池里面創(chuàng)建一個實例對象并對這個對象發(fā)送autorelease消息,無需再對對象進行內(nèi)存管理,對象會在釋放池最后釋放掉。
    如 KNZPerson *person = [[[KNZPerson alloc]init]autorelease];

  • 用于創(chuàng)建iOS和macOS應(yīng)用的蘋果UI框架、尤其是AppKit和UIKit,能夠自動提供自動釋放代碼塊。

  • 需要在自動釋放池中手動編寫自動釋放代碼塊:

    • 你編寫的程序不是以蘋果UI框架為基礎(chǔ)的,如命令行工具
    • 你實現(xiàn)的邏輯中含有創(chuàng)建許多臨時對象的循環(huán)。
    • 你編寫的應(yīng)用派生了一個或多個輔助線程。

自動管理ARC

自動引用計數(shù)是一種功能強大的內(nèi)存管理工具。與MRR相同,也是通過對象引用計數(shù)器來管理對象保留及釋放,編譯程序時由編譯器分析源代碼,在編譯代碼必要位置自動插入retain和release消息。
蘋果公司推薦使用ARC進行內(nèi)存管理。

ARC使用規(guī)則和約定

  • 不能手動編寫發(fā)送retain、retainCount、release、autorelease、dealloc消息
  • 不能直接進行id和(void *)類型的互換
  • 需要使用自動釋放池代碼塊執(zhí)行由ARC管理的自動釋放操作
  • 不能使用C結(jié)構(gòu)體重的對象指針。
  • 不能使用內(nèi)存去NSZone
  • 為了與非ARC代碼協(xié)作,不能創(chuàng)建以“copy”開頭的方法和自動聲明屬性
  • 默認情況下,ARC并非異常安全。

ARC的生命周期限定符

用來聲明常規(guī)變量和屬性的生命周期

  • 常規(guī)變量生命周期
    • _strong,默認配置
    • _weak,表面對象隨時被釋放,當對象被釋放,指針變量會被設(shè)置為nil
    • _unsafe_unretained,與_weak類似,但指針變量不會設(shè)置為nil而是懸掛
    • _autorelease,與autorelease方法無關(guān),用于通過引用傳遞對象。
  • 屬性生命周期
    • strong,默認配置,等同于retain
    • weak,類似于assign特性,如果引用對象被釋放了,其實例變量會被設(shè)置為nil。

循環(huán)引用解決方法weak

  • 設(shè)計類時,類A擁有類B的實例變量,類A強stong指向類B的實例變量;當類B也擁有類A的實例變量,這個時候用類B也用strong指向類A的實例變量話,那么就無法釋放A和B的實例對象了,這個時候需要有一方weak弱指向?qū)Ψ?,當對象A釋放后,對象B弱weak指向?qū)ο驛,對象B指針被設(shè)置為nil,對象B也會被釋放的。

  • 一般iOS開發(fā)界面UI控件都用weak,因為添加到window的控件都會被Window強引用著,沒有必要再使用一個指針強引用著UI控件。

  • delegate屬性一般weak特性,因為帶有代理設(shè)計的tableview或者textField之類的UIView控件都會讓控制器成為它的代理,控制器已經(jīng)有強指針指著UIView根視圖,根視圖再指向帶有代理設(shè)計的tableview或者textField之類的UIView控件,這個時候使用delegate再strong指向控制器的話就會造成循環(huán)引用。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容