Objective-C NSArray 配列 【初級編 第20回】

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


Person.m

#import "Person.h"

@implementation Person

    -(id)init{
        self = [self initWithWeight:65 height:1.71 name:@"nanashi"];
        if(self != nil){
        }
        return self ;
    }

    -(id)initWithWeight:(double)personWeight height:(double)personHeight{
        self = [self initWithWeight:personWeight height:personHeight name:@"nanashi"];
        if(self != nil){
        }
        return self;
    }
//指定イニシャライザ
    -(id)initWithWeight:(double)personWeight height:(double)personHeight name:(NSString*)personName{
        self = [super init];
        if(self != nil){
            weight = personWeight;
            height = personHeight;
            name = [NSString stringWithString:personName];
        }
        return self;
    }
    //アクセサ
    -(double)weight{
        return weight;
    }
    -(void)setWeight:(double)personWeight{
        weight = personWeight;
    }
    -(double)height{
        return height;
    }
    -(void)setHeight:(double)personHeight{
        height = personHeight;
    }
    -(NSString*)name{
        return name;
    }
    -(void)setName:(NSString*)personName{
        name = [NSString stringWithString:personName];
    }
    -(void)dealloc{
     [super dealloc];
    }
@end

指定イニシャライザとイニシャライザを作成していますね。
引数の文字列を基に「stringWithStrign」を使用して作成しています。

main.m

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

int main(void){

    int i = 0;
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSArray *ar = [NSArray arrayWithObjects:
                    [[[Person alloc] initWithWeight:65.0 height:1.80 name:@"Suzuki"] autorelease],
                    [[[Person alloc] initWithWeight:90 height:1.62 name:@"Satoh"] autorelease],
                    [[[Person alloc] initWithWeight:57.0 height:1.72 name:@"Yamada"] autorelease],
                    nil];

    printf("%dn",[[ar objectAtIndex:0] retainCount]);
    Manage *manage = [[Manage alloc] init];
    for(i = 0; i < [ar count]; i++){
        [manage setPerson:[ar objectAtIndex:i]];
    }
    [manage showBestPerson];
    [manage release];

    Person *p=[ar objectAtIndex:1];
    [pool drain];

    printf("%dn",[p retainCount]);
    return 0;
}

11行目でNSArrayインスタンスを作成しています。今回は「arrayWithObjects」の方を使用しています。
12行目から15行目は引数となっています。Person型のオブジェクトを体重と身長と名前を指定して作成しています。
このときautoreleaseを使用して自動解放プールに登録されるようにしているのは理由があります。
作成されたPerson型のオブジェクトは当然ながらmain内でallocされているので現在のプログラムが所有権を持ちます。
この時点でretainCountは1ですね。さらに上の方のNSArrayクラスの説明のところにかいたように、配列の要素に関しては
NSArrayオブジェクトが所有権を持ちます。今回の場合、「ar」ですね。ということはretainCountは2ということに
なります。
arがreleaseされただけではarが所有権を破棄する(retainCountを-1する)だけとなります。この時点でretaiCountは1です。
そのため中のPersonオブジェクトが完全に解放されるわけではありません。今回のような短いプログラムではあまり問題となりませんがもっと長く続くプログラムでは終了するまでずっとメモリに確保されたままとなります。
そこで自動解放プールに登録しています。
15行目は配列の最後はnilを入れないといけないというルールに従っています。

17行目は「objectAtIndex」を使用して配列の中のPersonオブジェクトを取り出し、そのretainCountを確認しています。
「objectAtIndex」の引数で0を指定しているので先頭のデータということになります。
上で書いたような現在のプログラムとarの2つが所有者になっているのかを確認するために入れています。

19行目は繰り返し構文で順番にManageクラスへ配列中のPersonオブジェクトを渡しています。繰り返し処理の回数は
配列内のデータ数分なので[ar count]を使用して配列の要素数を取得しています。
20行目は添え字をループするごとに増やすことで先頭から順番に値を取り出しManageオブジェクトにsetPersonメッセージを
送信しています。値の取り出しは17行目と同じですね。
配列にすることでこのような使い方ができます。もしそれぞれ別の変数として作成していた場合、setPersonメッセージを
その数だけ記述する必要があります。こんな感じですね。

Person *p1,*p2,*p3,*p4;
//間にインスタンス化の処理


[p1 setPerson];
[p2 setPerson];
[p3 setPerson];
以下ユーザ数分続く
・
・

3人くらいなら大したことはありませんが、数が多くなると不便です。

25行目と28行目は検証のためだけに入れている処理です。12行目から14行目にautoreleaseを入れてない場合と
autoreleaseを入れた場合で結果が変わるのを確認するためのものです。
26行目で自動解放プールが解放されるため配列の解放処理が行われます。
その後でもretainCountが1になっているということは配列内のオブジェクトが解放されていないということになります。

結果の確認

autoreleaseの処理が入っている場合
Objc20-1

autoreleaseの処理が入らなかった場合
Objc20-2

最初にretainCountを表示しているところの結果は2となっていますね。
これは現在のプログラムと配列がそれぞれ所有権を持っているということになりますね。
autoreleaseをしている場合は、retainCountを見るとセグメンテーション違反になっています。解放されているからですね。
しかしautoreleaseしていない場合は、retainCountが1になっています。
つまり配列が解放されても中身のインスタンスが残ったままということになります。

配列に解放処理の必要なオブジェクトを格納する場合は、気を付ける必要があります。

まとめ

・Objective-Cで配列を使用する場合は「NSArray」を使用する
・NSArrayはimmutableでmutableな配列を扱う場合は、NSMutableArrayを使用する
・入れるの中のオブジェクトは配列自身も所有権をもつ
・配列内でオブジェクトを作成している場合は解放処理に気をつける
といったところでしょうか。

ページ:
1

2

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

PAGE TOP