Objective-C メモリ管理,自動解放プール,autorelease 【初級編 第15回】

この記事は2012年12月4日に書かれたものです。内容が古い可能性がありますのでご注意ください。


Objective-C メモリ管理,自動解放プール,autorelease 【初級編 第15回】

retainCount以外のメモリ管理について

前回はretainCountによるObjective-Cでのメモリの管理について述べました。
所有権を必要とする場合は、retainメッセージによりretainCountを増やして別の部分で解放されてもヒープ領域が解放されないようにしていました。しかしその分、解放処理に気を配る必要があります。
そこでプログラム作成者が自身で管理する部分を減らす方法があります。
それが自動解放プールというものです。

自動解放プールについて

Objective-Cには自動解放プールという機能が備わっています。この機能は作成したオブジェクトにあらかじめ解放の予約を
しておくような形となります。1つのオブジェクトだけではなく複数のオブジェクトが解放予約状態にでき、
最後に予約していたオブジェクトをまとめて解放するという仕組みとなります。
この機能を使用するにはどうするのかを説明したいと思います。

  1. 自動解放プールの作成
    まず自動解放プールというものを作成します。
    NSAutoreleasePoolというオブジェクトが自動解放プール用のオブジェクトになります。
  2. オブジェクトを自動解放プールに登録する
    次に必要なオブジェクトを作成することになるのですが、その作成したオブジェクトをこの自動解放プールに登録します。
    解放しなければならないオブジェクト一覧のリストに書きこむ感じですね。
    自動解放プールを使用しない場合、オブジェクトの作成時にretainCountが1となり所有権を得ることになりますが、
    自動解放プールに登録すると所有権を破棄する形となります。しかし所有権は破棄されますが、retainCountが0になるわけでは
    なく即座にメモリの解放が行われるわけではありません。
    自動解放プールに登録といっても自動解放プールオブジェクトに直接メッセージを送るのではなく、作成したオブジェクトに
    「autorelease」メッセージを送信することで登録を行います。
    このメソッドはNSObjectで定義されています。以下のようになっていました。

    -(id)autorelease NS_AUTOMATED_REFCOUNT_UNAVAILABLE
    
  3. 自動解放プールの解放
    このままではプログラムが終了するまでメモリが確保されたままとなりますので、自動解放プールを解放します。
    このときにリストに登録されているオブジェクトのメモリが一斉に解放されることとなります。
    逆に自動解放プールが解放されるまでは確保したままとなりますので登録したから大丈夫というわけではなく自動解放プールは
    解放する必要があります。このタイミングで各オブジェクトにreleaseが送られることになります。

コードでの確認

では実際のコードで見てみます。前回のコードの一部を変更します。
比較のために前回のコードのretainなどのところはコメントアウトしています。

Manage.m

#import <Foundation/Foundation.h>
#import <math.h>
#import "Manage.h"
#import "Bmi.h"

@implementation Manage
-(Person*)person{
    return person;
}
-(void)setPerson:(Person*)p{
    double num;
    if(person!=nil){
        num = fabs(22 - [Bmi calcBmi:[person weight] height:[person height]]) -
            fabs(22 - [Bmi calcBmi:[p weight] height:[p height]]);
        if( num > 0){
            //[person release];
            person=p;
            //[person retain];
        }
    }else{
        person=p;
        //[person retain];
    }
}
-(void)showBestPerson
{
    printf("weight:%2.1fn",[person weight]);
    printf("height:%2.1fn",[person height]);
    printf("Bmi:%2.1fn",[Bmi calcBmi:[person weight] height:[person height]]);
}
-(void)dealloc{
    //[person release];
    [super dealloc];
}
@end

とりあえずManage.mでretainしている部分をコメントアウトしています。

main.m

#import <Foundation/Foundation.h>
#import "Bmi.h"
#import "Person.h"
#import "Manage.h"

int main(void){
    id autoPool = [[NSAutoreleasePool alloc] init];
    //Person *teacherN = [[Person alloc] initWithWeight:56.0 height:1.72];
    Person *teacherN = [[[Person alloc] initWithWeight:56.0 height:1.72] autorelease];
    printf("TeacherN BMI:%2.1fn",[Bmi calcBmi:[teacherN weight] height:[teacherN height]]);
    printf("retainCount:%dn",[teacherN retainCount]);

    Manage *manage = [[Manage alloc] init];
    [manage setPerson:teacherN];
    printf("retainCount:%dn",[teacherN retainCount]);
    //[teacherN release];
    [manage showBestPerson];
    [manage release];
    printf("retainCount:%dn",[teacherN retainCount]);

    [autoPool release];
    printf("retainCount:%dn",[teacherN retainCount]);
    return 0;
}

mainでは7行目で自動解放プールを作成し、ID型の変数autopoolに格納しています。
8行目のPerson型のteacherNオブジェクトを作成している部分を変更して、
9行目のように作成と同時に「autorelease」メッセージを送っています。
こうすることでautopoolにteacherNが登録されることになります。
今回は自動解放プールにより解放されるのかどうかを確認する目的なので、
16行目でreleaseしていたのをコメントアウトしています。
20行目で作成した自動解放プールを解放しています。ここで登録されているteacherNが解放されるはずです。

実行結果の確認

実行してみます。

「autorelease」の後にteacherNのretainCountを見ても0にはなっていません。
解放はされてないということですね。
自動解放プールを解放した後にretainCountを見てみるとセグメンテーション違反となっています。
ちゃんと解放されているということですね。

今回はteacherNに対してのみ行っていますが、manageも自動解放プールに登録することができます。
13行目でmanageに対して「autorelease」メッセージを送信するようにして、
18行目の解放処理をコメントアウトすればOKです。

自動解放プールの注意点

自動解放プールが解放されるまではメモリが解放されないので、プログラムの最後までずっと確保しっぱなしの場合、
不必要となったオブジェクトが無駄にメモリを使っているという状況になるため、使い所に気を付ける必要があります。

あとautoreleaseは上でも書いたように自動解放プールが解放されるときにreleaseを送る形となるので
retainCountを-1するだけです。自動解放プールが解放されるときにretainCountが2以上になっているものがあった場合、
-1されるだけなので解放されません。その時はretainをした回数autoreleaseすれば解放されます。

今回のまとめとしては

  • 自動解放プールを使用することでオブジェクトのrelease処理をまとめて行うことができる
  • 使用するにはNSAutoreleasePoolオブジェクトを作成する
  • 自動解放プールにオブジェクトを登録するには作成したオブジェクトにautoreleaseメッセージを送る
  • 自動解放プールに登録されたオブジェクトがreleaseされるのはNSAutoreleasePoolが解放されるときである
  • 使い方によってはメモリをずっと保持したままになるため気を付ける必要がある

といったところでしょうか。

  • このエントリーをはてなブックマークに追加

PAGE TOP