メモリ管理の基本概念
高級言語では、例えばC#のような言語はガベージコレクション(GC)によってメモリ管理を自動化していますが、Objective-Cには同様の自動管理機能はありません。したがって、プログラマ自身が手動でメモリ管理を行う必要があります。
ARCとMRCの違い
Xcode 4.2以降ではARC(Automatic Reference Counting)が導入され、コンパイル時に自動的にメモリ解放コードが挿入されるようになりました。手動での解放コードを記述するとコンパイルエラーになります。本稿ではMRC(Manual Reference Counting)について説明するため、ARCを無効化する必要があります。設定手順は以下の通りです: プロジェクト設定 → Build Settings → "Automatic Reference Counting" を検索し、Objective-C Automatic Reference CountingをNoに設定します。
参照カウンタの仕組み
C#ではGCが自動的に不要なオブジェクトを回収しますが、Objective-Cでは参照カウンタ方式を使用しています。各オブジェクトには内部的に整数値(retainCount)が存在し、オブジェクト作成直後の値は1です。alloc、retain、new、copyなどのメソッド呼び出しによりカウンタは増加し、releaseメソッド呼び出しにより減少します。カウンタが0になると、システムは自動的にdeallocメソッドを呼び出してオブジェクトを破棄します。
以下は簡単な実装例です:
Personクラスのヘッダファイル:
@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
- (void)dealloc;
@end
Personクラスの実装ファイル:
@implementation Person
- (instancetype)init {
self = [super init];
if (self) {
NSLog(@"Personインスタンスが作成されました");
}
return self;
}
- (void)dealloc {
NSLog(@"Personインスタンスが破棄されました");
}
@end
メモリ管理の原則
手動でのメモリ管理は複雑になりがちです。基本的な原則は「作成者は責任を持って解放する」ことです。オブジェクト間の循環参照や複雑な関係性がある場合でも、この原則に従うことで適切な管理が可能になります。
車を所有するPersonクラスの例:
Carクラス定義:
@interface Vehicle : NSObject
@property (nonatomic, assign) NSInteger speed;
- (void)startEngine;
@end
@implementation Vehicle
- (instancetype)init {
self = [super init];
if (self) {
_speed = 0;
}
return self;
}
- (void)dealloc {
NSLog(@"Vehicleが破棄されました");
}
@end
Personクラスのプロパティ設定:
@interface Person : NSObject
@property (nonatomic, retain) Vehicle *ownedVehicle;
@end
@implementation Person
- (void)setOwnedVehicle:(Vehicle *)vehicle {
if (_ownedVehicle != vehicle) {
[_ownedVehicle release];
_ownedVehicle = [vehicle retain];
}
}
- (void)dealloc {
[_ownedVehicle release];
[super dealloc];
}
@end
プロパティの属性設定
@property宣言で使用可能な属性には以下の種類があります:
- nonatomic - スレッドセーフでない高速なアクセス
- atomic - スレッドセーフなアクセス(デフォルト)
- assign - 基本データ型用
- retain - オブジェクト型用
- copy - 文字列やブロック用
具体的な使用例:
@property (nonatomic, retain) Vehicle *currentVehicle;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, copy) NSString *fullName;
自動解放プールの利用
Objective-Cにはautoreleaseという半自動の解放機構があります。@autoreleasepoolブロック内でautoreleaseメソッドを呼び出したオブジェクトは、ブロック終了時に自動的にreleaseされます。
int main() {
@autoreleasepool {
Person *individual = [[[Person alloc] init] autorelease];
Vehicle *automobile = [[[Vehicle alloc] init] autorelease];
// ブロック終了時に両オブジェクトが解放される
}
return 0;
}
autoreleaseメソッドは参照カウンタを変更せず、オブジェクトを解放プールに登録するだけです。プールが破棄される際にまとめてreleaseが呼び出されますが、カウンタが0にならない限りオブジェクトは解放されません。
また、大量のメモリを消費するオブジェクトや多数のオブジェクトを同時に扱う場合は、複数のプールに分けることを推奨します。