你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> Objective-C內存管理[iOS]

Objective-C內存管理[iOS]

編輯:IOS開發綜合

 


\

 <喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoMSBpZD0="1-基本原理">1 基本原理

1.1 什麼是內存管理

移動設備的內存極其有限,每個 app 所能占用的內存是有限制的 當app所占用的內存較多時,系統會發出內存警告,這時得回收一些不需要再使用的內存空間。比如回收一些不需要使用的對象、變量等。 管理范圍:任何繼承了 NSObject 的對象,對其他基本數據類型(如int, char, float, double, struct, enum 等)無效

1.2 對象的基本結構

每個對象都有一個引用計數器,是一個整數,表示“對象被引用的次數”,即有多少人正在使用這個Objective-C對象。 每個Objective-C對象內部專門有4個字節的存儲空間來存儲引用計數器。

1.3 引用計數器的作用

當使用 alloc, new 或者 copy 創建一個新對象時,新兌現的引用計數器默認就是 1。 當一個對象的引用計數器值為 0 時,對象占用的內存就是被系統回收,換句話說,如果對象的計數器不為 0,那麼在整個程序運行過程中,它占用的內存就不可能被回收,除非整個程序已經退出。

1.4 引用計數器的操作

給對象發送一個 reatin 消息,可以使引用計數器值 +1 (retain 方法返回對象本身) 給對象發送一個 release 消息,可以使引用計數器值 -1 可以給對象發送 retainCount 消息,獲得當前的引用計數器值

1.5 對象的銷毀

當一個對象的引用計數器值為 0 時,那麼它將被銷毀,其占用的內存被系統回收 當一個對象被銷毀時,系統會自動向對象發送一條 dealloc 消息 一般會重寫 dealloc 方法,在這裡釋放相關資源,dealloc 就像對象的遺言 一旦重寫了 dealloc 方法,就必須調用 [super dealloc],並且放在最後面調用 不要直接調用 dealloc 方法

2 Xcode 設置

對野指針發送消息 這種錯誤行為在進行時進行報錯,需要設置 Xcode

 


\

 

 


 

為了確定你的代碼在編譯時出現問題,則可以使用 Clang Static Analyzer ,內置在Xcode中。

許多工具和技術的技術說明在 Technical Note TN2239 中有描述,iOS Debugging Magic ,特別是使用的 NSZombie ,以幫助找到還未釋放的對象。 您可以使用儀器來跟蹤引用計數的事件,並查找內存洩漏。請參閱 Collecting Data on Your App

3 內存管理原則

3.1 原則分析

只要還有人在用某個對象,那麼這個對象就不會被回收 只要你想用這個對象,就讓對象的計數器 +1 當你不再使用這個對象時,就讓對象的計數器 -1

3.2 誰創建,誰 release

如果你通過 alloc, new 或 [mutable]copy 來創建一個對象,那麼你必須調用 release 或 autorelease 換句話說,不是你創建的,就不用你去 [auto]release

3.3 誰 retain ,誰 release

只要你調用了 retain ,無論這個對象是如何生成的,你都要調用 relaese

3.4 總結

有始有終, 有加就有減 曾經讓對象的計數器+1, 就必須在最後讓對象計數器-1

4 set 方法的內存管理

如果你有個Objective-C對象類型的成員變量 ,就必須管理這個成員變量的內容,比如有個 NSNumber *count

4.1 set 方法的實現

@interface Counter : NSObject
@property (nonatomic, retain) NSNumber *count;
@end;
- (NSNumber *)count {
    return _count;
}
- (void)setCount:(NSNumber *)newCount {
    [newCount retain];
    [_count release];
    // Make the new assignment.
    _count = newCount;
}

4.2 reset 方法的實現

- (void)reset {
    NSNumber *zero = [[NSNumber alloc] initWithInteger:0];
    [self setCount:zero];
    [zero release];
}
- (void)reset {
    NSNumber *zero = [NSNumber numberWithInteger:0];
    [self setCount:zero];
}

4.3 init 方法的實現

- init {
    self = [super init];
    if (self) {
        _count = [[NSNumber alloc] initWithInteger:0];
    }
    return self;
}
- initWithCount:(NSNumber *)startingCount {
    self = [super init];
    if (self) {
        _count = [startingCount copy];
    }
    return self;
}

4.4 dealloc方法的實現

- (void)dealloc {
    [_count release];
    [super dealloc];
}

5 @property 的內存管理

5.1 @property 參數

1.set方法內存管理相關的參數

retain : release舊值,retain新值(適用於OC對象類型) assign : 直接賦值(默認,適用於非OC對象類型) copy : release舊值,copy新值

2.是否要生成set方法

readwrite : 同時生成setter和getter的聲明、實現(默認) readonly : 只會生成getter的聲明、實現

3.多線程管理

nonatomic : 性能高 (一般就用這個) atomic : 性能低(默認)

4.setter和getter方法的名稱

setter : 決定了set方法的名稱,一定要有個冒號 : getter : 決定了get方法的名稱(一般用在BOOL類型)
@interface Person : NSObject

// 返回BOOL類型的方法名一般以is開頭
@property (getter = isRich) BOOL rich;

//   
@property (nonatomic, assign, readwrite) int weight;
// setWeight:
// weight

// 
@property (readwrite, assign) int height;

@property (nonatomic, assign) int age;

@property (retain) NSString *name;
@end

6 循環引用

6.1 @class

使用場景

對於循環依賴關系來說,比方A類引用B類,同時B類也引用A類

#import "B.h"
@interface A : NSObject {
    B *b;
}
@end
#import "A.h"
@interface B : NSObject {
    A *a;
}
@end

這種代碼編譯會報錯,當使用@class 在兩個類相互聲明,就不會出現編譯報錯

用法概括

使用 @class 類名;就可以引用一個類,說明一下它是一個類

和 #import區別

#import 方法會包含被引用類的所有信息,包括被引用類的變量和方法;@class 方式只是告訴編譯器在 A.h 文件中 B *b 只是類的聲明,具體這個類有什麼信息,這裡不需要知道,等實現文件中真正要用到的時候,才會真正去查看B類中的信息。

如果有上百個頭文件都 #import 了同一個文件,或者這些文件依次被#import,那麼一旦最開始的頭文件稍有改動,後面引用到這個文件的所有類都需要重新編譯一遍,這樣的效率比較差,相對來說,使用 @class 方式就不會出現這種問題了。

在 .m 實現文件中,如果需要引用到被引用類的實體變量或者方法時,還需要使用 #import 方式引入被引用類。

6.2 循環 retain

比如 A 對象 retain 了 B 對象, B 對象 retain 了 A 對象 這樣會導致 A 對象 和 B 對象永遠無法釋放

6.3 解決方案

當兩端互相引用時,應該一端用 retain , 一端用 assign

7 autorelease

7.1 autorelease 的基本用法

給某個對象發送一條 autorelease 消息時,就會將這個對象加到一個自動釋放池中 當自動釋放池銷毀時,會給池子裡面的所有對象發送一條 release 消息 調用 autorelease 方法時並不會改變對象的計數器,並且會返回對象本身 autorelease 實際上只是把對 release 的調用延遲了,對於每一次 autorelease ,系統只是把該對象放入了當前的 autorelease pool 中,當該 pool 被釋放時,該 pool 中的所有對象會被調用 release 。

7.2 自動釋放池的創建

iOS 5.0前
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

[pool release]; // [pool drain];
iOS 5.0後
@autoreleasepool
{

}
在程序運行過程中,可以創建多個自動釋放池,它們是以棧的形式存在內存中 Objective-C對象只需要發送一條 autorelease 消息,就會把這個對象添加到最近的自動釋放池中(棧頂的釋放池)

7.3 應用實例

與 release 的對比

以前:

Book *book = [[Book alloc] init];
[book release];

現在:

Book *book = [[[Book alloc] init] autorelease];
// 不要再調用 [book release];
一般可以為類添加一個快速創建對象的類方法
+ (id)book {
    return [[[self alloc] init] autorelease];
    // 上面這行本來可以寫成 return [[[Book alloc] init] autorelease];
    // 但是這樣無法滿足Book子類的創建需求,所以最好都寫成 self 
}

外界調用 [Book book] 時,根本不用考慮在什麼時候釋放返回的 Book 對象。

7.4 規律

一般來說除了 alloc, new 或 copy 之外的方法創建的對象都被聲明了 autorelease 比如下面的對象都已經是 autorelease 的,不需要再 release
NSNumber *n = [NSNumber numberWithInt:100];
NSString *s = [NSString stringWithFormat:@"jack"];

7.5 autorelease 的好處

不用再關心對象釋放的時間 不用再關心什麼時候調用release

7.6 autorelease 的使用注意

占用內存較大的對象不要隨便使用 autorelease 占用內存較小的對象使用 autorelease,沒有太大影響

7.7 錯誤寫法

1. alloc之後調用了 autorelease,又調用 release

@autoreleasepool
{
   // 1
   Person *p = [[[Person alloc] init] autorelease];

   // 0
   [p release];
}

2. 連續調用多次 autorelease

@autoreleasepool
{
   Person *p = [[[[Person alloc] init] autorelease] autorelease];
}
  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved