你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS開發入門 ☞ OC語言·筆記五

iOS開發入門 ☞ OC語言·筆記五

編輯:IOS開發綜合

MRC — 手動管理內存

1.1 內存引用平衡原則

1) 如果使用alloc,new開頭,或者是copy(復制一個對象)來創建一個對象,意味著你擁有這個對象的所有權。這個對象的引用計數器初始值為1(也有可能>1)。

2) 如果你擁有這個對象的所有權,在不使用此對象時,就有責任向對象發送release消息。(誰創建了對象,誰就有責任release這個對象)

3) 如果並不擁有一個對象的所有權,而想要使用這個對象,為了防止你在使用此對象期間,對象被別人釋放掉,需要向對象發送retain消息,以保持對象。此時可以認為,你也擁有了這個對象所有權。

4) 當你使用完retain過的對象後,有責任release一下這個對象。

(誰retain了一個對象,誰就有責任release這個對象)

       配對出現:(+1、-1 ==>平衡) 

我們創建的對象不用了,就release;我們retain的對象不用了,就release。

    內存管理的原則就是有加就有減。也就是說, 一次alloc(new)對應一次release, 一次retain對應一次release。

1.2 自動釋放池(autoreleasepool)

通過自動釋放池來管理對象,只需要一個自動釋放池,可以管理很多對象,當自動釋放池結束的時候,會自動向池中的每個對象都發送release消息。

1) 如果一個對象創建後,不能馬上釋放它,但又不得不盡到釋放對象的責任,此時可以將對象放入自動釋放池,延遲對象的釋放時機。比如絕大部分工廠方法都是如此。工廠方法中的對象是方法中創建的,按理來說應該由工廠方法內部釋放,但工廠方法的功能決定了這個對象不能馬上釋放,此時應該將對象放入自動釋放池。

2) 當自動釋放池結束時,會向池中的所有對象發送release消息,如果此時,池中的對象的引用計數器是1,那麼,對象會被釋放掉。

3) 如何開始和結束一個自動釋放池呢?

//自動釋放池,用於回收對象的存儲空間。

@autoreleasepool{ //開始(創建一個自動釋放池)

  ……  ……

} //結束(自動釋放池銷毀了, 給自動釋放池中所有的對象發送一條release消息)

      還有一種性能低下,被淘汰的自動釋放池創建方式(了解)

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];//開始一個自動釋放池

   ……  ……

[pool drain];//結束一個自動釋放池

4) 無論一個對象是否在自動釋放池,只要這個對象是由別人創建的,你要使用此對象,就得retain,用完此對象,就得release,即使對象在自動釋放池,也依然如此。

5) 實際開發中,合理使用自動釋放池來避免內存使用出現峰值。

如果App出現的內存使用的峰值,此時才考慮是否是由於大量使用工廠方法造成的,是否需要使用自動釋放池解決問題。要合理使用自動釋放池,大量使用會消耗系統資源。

6) autorelease方法:在自動釋放池中使用,目的是將對象添加到自動釋放池中。自動釋放池銷毀時會對池中對象作release操作。(延遲release)

@autoreleasepool {

Person *p = [[[Person alloc] init] autorelease];

        

}

  • autorelease方法與release方法的區別:這兩個方法都能把對象的引用計數器減1。
    • release是一個精確的減1,對對象的操作只能在release之前進行,如果是在之後,就會出現野指針錯誤;
    • autorelease是一個不精確的引用計數器減1,當給對象發送autorelease消息時,對象就會被放到自動釋放池中,自動釋放池銷毀時會給池中的所有對象發送release消息,使得所有對象的計數器減1,所以本質上autorelease還是會調用release。
  • 常見錯誤:

// 銷毀自動釋放池的時候 要對person再執行release操作的話 會報野指針錯誤

@autoreleasepool {

Person *person = [[[Person alloc] init] autorelease];

[person release];

// 對象執行兩次autorelease意味著自動釋放池銷毀的時候 對象會執行兩次release操作 會報野指針錯誤

@autoreleasepool {

Person *person = [[[[Person alloc] init] autorelease] autorelease];

1.3 setter方法的內存管理(應用場景:兩個類是聚合關系)

當一個方法傳入一個對象後,如果需要將這個對象用實例變量等手段保存起來持續使用時,需要做以下事:

1) 先將此對象的引用計數器加1(retain)

2) 再將原來實例變量指向的對象的引用計數器減1(release)

3) 最後將傳入的對象地址保存到實例變量中

4) 被retain的對象通常需要在dealloc方法中release.

 

只要一個對象想使用房間,就需要對這個房間的引用計數器+1

只要一個對象不想使用房間,就需要對這個房間的引用技術器-1

      

經常會被問到的問題:

1) 前面3個順序可否顛倒?

能不能先release原來的對象,再賦值,最後retain新對象? 一般可以,但不建議

       

2) dealloc方法中,可不可以使用self.屬性 = nil;的方式釋放屬性所指向的對象? ok的

可以的,self.屬性 相當於調用上面的setter方法(該方法中有向對象發release)

    Demo:setter方法的內存管理

     

    

1.4 property修飾符

1) nonatomic 非原子性操作 (安全性低,但效率高。IOS實際開發中99%用這個,以確保性能)

atomic 原子性操作 (安全性高,但很耗費資源)

注意,默認是原子性的(atomic),所以在定義屬性時一定要寫上nonatomic。

2) assign, retain, copy

    assign  是默認值,僅做賦值。不會解決內存問題(即不會retain,也不會release)。在MRC中可用於對象類型和非對象類型。

    retain 只能用於對象類型的屬性,會解決內存問題(生成的setter方法會自動加入retain,release等內存操作代碼)

    copy 一些特殊對象類型,如果不希望和別人共享一個對象,用copy會自動創建一個新的對象。

      有些屬性在傳入後,需要拷貝一份,以防止傳入的對象內容被修改後,影響到我的對象。

(並不是所有的對象都能拷貝,只有遵守<NSCopying>協議,實現了協議中CopyWithZone方法的這種對象才擁有拷貝的功能)

3) readonly/readwrite

readwrite 是默認的,編譯器生成getter和setter方法

readonly 只讀,編譯器只生成getter方法

  屬性訪問器的類型:

  

注:聲明屬性默認情況下,並沒有解決內存問題

當使用 @property(retain)引用數據類型…,幫我們解決了setter使用中的內存問題,但dealloc中的release操作,我們自己來做。

1.5  MRC中的循環引用(兩個對象互相引用(即互相包含)時,要一強一弱!)

  如果A對象要擁有B對象, 而B對象又要擁有A對象, 此時會形成循環retain

  如何解決這個問題:讓其中一方不要做retain操作即可!

Demo:

   

ARC — 自動管理內存

1.1 概念

Automatic Reference Counting  自動引用計數

基於MRC, 在MRC下,對象的引用計數是由程序員負責的。在ARC下,對象的引用計數工作由編譯器來完成。

1.2 ARC的工作原理

在ARC下,堆內存空間的管理依然使用引用計數器的方式。只是ARC下的引用計數器的加減工作不再由程序員來做,而是由編譯器來做。

編譯器在編譯程序期間,會在程序恰當的位置自動加入對對象的retain,release和autorelease的操作。

注意:ARC是 編譯期語法或編譯器特性(即可以理解為是Xcode的一個功能),而不是運行時特性。

1.3 怎麼用

  程序員不要對對象的引用計數器進行操作,編譯器會幫我們做:

    1)  在ARC下,不要在程序中調用retain, release, autorelease方法。

    2)  在ARC下,不要重寫retain, release, autorelease方法。

    3)  在ARC下,不要在dealloc方法中調用父類的dealloc方法。實際上,ARC下,dealloc方法基本沒有用了。

      總之,一切與內存操作相關的東西都由ARC自動完成。

1.4 ARC的判斷原則:(即系統怎麼判斷對象是否要釋放)

  • 自動管理內存的判斷原則:只要還有一個強指針變量指向對象,對象就會保持在內存中。
  • 強指針(強引用):
    • 默認情況下所有的指針變量都是強指針
    • 被__strong修飾的指針
    • 例如:

TRPerson *p1 = [[TRPerson alloc]init];

__strong TRPerson *p2 = [[TRPerson alloc]init];

 

  • 弱指針(弱引用)
    • 被__weak修飾的指針
    • 例如:

__weak TRPerson *p = [[TRPerson alloc]init];

 

  • 舉例:

//ARC的判斷准則:只要沒有強指針指向對象,對象就會釋放

TRPerson *p1 = [[TRPerson alloc]init];

__strong TRPerson *p2 = [[TRPerson alloc]init];

__weak TRPerson *p3 = p2;

p2 = nil;//p2改變了指向,此時就沒有強指針指向對象,對象就會釋放

}//出了大括號,局部變量p1就釋放,此時就沒有強指針指向對象,對象就會釋放

        單個對象的內存管理:如果一個對象不再使用了,就把指向對象的強指針置為nil,對象就會釋放。

  • 注意:
    • 當使用ARC的時候,暫時忘記“引用計數器”,因為判斷標准變了。
    • 在實際開發中,千萬不要使用一個弱指針來保存一個剛剛創建的對象。

{

//p是弱指針,對象會被立即釋放

__weak TRPerson *p = [[TRPerson alloc]init];//剛創建就被釋放!

}

 

1.5 ARC中多個對象的內存管理:

  • ARC和MRC一樣,想擁有某個對象必須用強指針保存對象,但是不需要在dealloc方法中release

@class TRDog;

@interface TRPerson : NSObject

 

//MRC下寫法

@property (nonatomic, retain) TRDog *dog;

//ARC下寫法

@property (nonatomic, strong) TRDog *dog;

 

@end

MRC

A對象想使用B對象, 需要對B對象進行一次retain

A對象不用B對象了, 需要對B對象進行一次release

即,property的時候進行retain, dealloc的時候進行release

 

ARC

A對象想使用B對象, 那麼就需要用一個強指針指向B對象(即用strong,表示強引用)

// 在ARC中保存一個對象用strong, 相當於MRC中的retain

@property (nonatomic, strong) Dog *dog;

A對象不用B對象了, 什麼都不需要做, 編譯器會自動幫我們做

 

1.6 ARC下循環引用問題

  循環引用:指兩個對象相互強引用了對方,即retain了對方,從而導致誰也釋放不了誰的內存洩露問題。

  • ARC和MRC一樣,如果A擁有B,B也擁有A,那麼必須一方使用弱指針
  • 兩個強指針互相引用,兩個空間就會永不釋放!所以必須要一強一弱。

@class TRDog;

@interface TRPerson : NSObject

//MRC寫法

//@property (nonatomic, retain) TRDog *dog;

//ARC寫法

@property (nonatomic, strong) TRDog *dog;

@end

 

@interface TRDog : NSObject

//錯誤寫法,循環引用會導致內存洩露

//@property(nonatomic, strong)TRPerson *owner;

 

//正確寫法,當如果保存的是對象類型建議使用weak

//@property(nonatomic, assign)TRPerson *owner;

@property (nonatomic, weak) TRPerson *owner;

@end

  • ARC下循環引用的案例:

    

1.7 如果在ARC下定義屬性的內存特質(attribute)

  • 在MRC下, 與內存相關的屬性特質有:assign, retain, copy
  • 在ARC下, 與內存相關的屬性特質有:

(1)strong  強引用 

  類似於retain,引用時候會引用計數+1。

strong 案例:

@property (nonatomic, strong) NSString *str1;

@property (nonatomic, strong) NSString *str2;

self.str1 = @"Hello World";

self.str2 = self.str1;

self.str1 = nil;

NSLog(@"str2 = %@", self.str2);

結果是:str2 = Hello World

(2)weak   弱引用

  • 類似於assign,不會改變引用計數,只做簡單的賦值。
  • weak與assign的區別:
    • assign 僅做賦值(默認值),不會解決內存問題。在MRC中可用於對象類型和非對象類型。
    • weak只用於ARC下的對象類型(即帶*號的指針類型)。weak比assign更完全,會自動把野指針置空。
    • assign一般在ARC下用於基本數據類型(即不帶*號的類型,包括id類型)
    • weak 的特點:
      • 聲明為weak的指針,指針指向的地址一旦被釋放,這些指針都將被賦值為nil。這樣的好處能有效的防止野指針。
      • 特定情況下,如果內存的釋放會出現問題,經常使用weak來解決,比如,最常見的問題是"內存循環引用"

weak 案例:

@property (nonatomic, strong) NSString *str1;

@property (nonatomic, weak) NSString *str2;

self.str1 = @"Hello World";

self.str2 = self.str1;

self.str1 = nil;

NSLog(@"str2 = %@", self.str2);

結果是:str2 = null

(3)unsafe_unretained  (從名字上來看:不安全,不retain)

  • 默認的, 可以用於非對象類型/對象類型。
  • 用於非對象類型時和assign一樣,只做簡單的賦值。
  • 用於對象類型時類似於weak,但沒有weak安全(因為當指向的對象被銷毀時,指針不會自動置空,會產生野指針錯誤)

unsafe_unretained 案例:

@property (nonatomic, strong) NSString *str1;

@property (nonatomic, unsafe_unretained) NSString *str2;

self.str1 = @"Hello World";

self.str2 = self.str1;

self.str1 = nil;

NSLog(@"str2 = %@", self.str2);

沒有輸出結果!程序崩掉,會報野指針錯誤(EXC_BAD_Access 壞訪問)

(4)copy   拷貝 (和以前的copy一樣)

    使用copy: 對NSString

    效果其實和retain沒什麼兩樣,唯一的區別就是copy只用於NSString而不能用於NSMutableString, 如果當一個類繼承NSObject,那麼這個類裡面的屬性需要使用copy。

    retain是指針拷貝,copy是內容拷貝。

  • strong, weak, unsafe_unretained往往都是用來聲明屬性的。

    如果想聲明臨時變量就得用__strong, __weak, __unsafe_unretained, __autoreleasing,其用法與上面介紹的類似。

案例:

__strong NSString *str1 = @"Hello World";

__weak  NSString *str2 = str1;

__unsafe_unretained NSString *str3 = str2;

str1 = nil;

//現在str1與str2的指針都為nil,而str3不為nil,但是是野指針。

提示:沒有兩個下劃線的放屬性裡,有兩個下劃線的放變量前。

  • objective-c內存管理中有一條是:誰分配誰釋放。__autoreleasing則可以使對像延遲釋放。

      autoreleasing的應用:在函數內部申請的空間,在函數外部也可以使用

 

//MRC下

-(NSString *)stringTest

{

NSString *retStr = [NSString stringWithString:@"test"];

return [[retStr retain] autorelease];

}

//ARC下

-(NSString *)stringTest  

{

__autoreleasing NSString *retStr = [NSString alloc] initWithString:@"test"];  

return retStr;  

}

  • 總結:

    ARC下@property參數:

      strong:用於OC對象,相當於MRC中的retain (強引用)

      weak:用於OC對象,相當於MRC中的assign  (弱引用)

      assign:用於基本數據類型,跟MRC中的assign一樣。

 

1.8 ARC下的自動釋放池

在ARC下,對象的工廠方法依然會將對象放入自動釋放池。當池結束時,向池中的對象發送release消息。

池中的對象什麼時候銷毀,無法確定,因為編譯器會做很多優化。

在IOS開發中,一個事件循環結束,自動釋放池會釋放一次,池中的對象會收到release消息。

 

【iOS開發入門 ☞ OC語言&#183;筆記五】的相關資料介紹到這裡,希望對您有所幫助! 提示:不會對讀者因本文所帶來的任何損失負責。如果您支持就請把本站添加至收藏夾哦!

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved