web-dev-qa-db-ja.com

Objective CでNSMutableArrayを初期化する方法は?

私はJavaのバックグラウンドから来て、Objective Cを学んでいます。文字列配列と、配列を変更するためのメンバー関数を持つクラスを作成しようとしています。コードは次のようになりました。

@implementation TAWChapter

@synthesize mSubject;

@synthesize mItems;

- (id) init{
    self.mItems = [[NSMutableArray alloc] init];
    return self; 
}

- (void) setSubject:(NSString *)subject{
    self.mSubject = subject; 
}

- (void) addItem:(NSString *)item{
    [self.mItems addObject:@"asdf"]; 
}

@end

それはうまくいきませんでした。私は得た "[__NSArrayI addObject:]: unrecognized selector sent to instance ""NSInvalidArgumentException"。インターネットで検索した後、コンストラクターの1行を次のように変更しました。

self.mItems = [self.mItems init];

それはうまくいきましたが、なぜですか? Java開発者の観点からは、最初の行は2番目の行よりも理にかなっています。別の行がありますが、最初の行と同じですが、機能しています(コンストラクターではありません)。誰かが私にこれを説明してください?

11
Xi Zhang
  1. まず、Objective-Cのコーディング規則に従う必要があります。 Objective-Cでは、クラスにはコンストラクターがなく、イニシャライザーがあります。 Objective-Cでは、イニシャライザーはスーパークラスのイニシャライザーを呼び出すため、次のようになります。

    - init
    {
        self = [super init];
        if (!self) return nil;
    
        // set up other stuff here 
    
        return self;
    }
    
  2. 次に、ARCを使用していない限り、メモリリークが発生する可能性があります。イニシャライザーの最初の行は、所有しているオブジェクトを、所有権を取得する可能性が高いプロパティに割り当てます。次のいずれかを使用する必要があります。

    // property takes care of ownership
    self.mItems = [NSMutableArray array];
    

    または:

    // assign to instance variable directly with owned object
    mItems = [[NSMutableArray alloc] init];
    

    Appleは、KVOのようなものをいじることができるため、初期化子でのアクセサメソッドの使用を推奨しない場合がありますが、アクセサメソッドを一貫して使用することで、適切なオブジェクト所有権とメモリ管理が保証されます。

  3. イニシャライザーの行を次のように変更します。

    self.mItems = [self.mItems init];
    

    します何も。イニシャライザーメソッドが呼び出されると(通常は割り当てられた直後)、すべてのインスタンス変数が自動的にnilに設定されます。だからあなたがしていることはただです:

    self.mItems = [nil init];
    

    これはただ:

    self.mItems = nil;
    

    また、最初にインスタンスを割り当てずにinitを使用したり、initを2回以上使用したりしないでください。

  4. スーパークラス自体を初期化させないと、他の領域で問題が発生する可能性があります。 Build&Analyzeを実行し、アナライザーによって指摘された問題が修正されていることを確認します。

28
dreamlax

Objective-cはcのスーパーセットであるため、基本的には「OO」構文の糖衣構文を持つcです。オブジェクトを作成(または使用)する前に、ヒープ内にオブジェクト用のスペースを割り当てる必要があります。これは[Class alloc]で行います。次のステップは、そのスペースの初期化です。 allocは、ヒープ内のそのスペースへのポインターを返します。これは、initで初期化します;)

したがって、Class *myObjc = [[Class alloc] init];を呼び出します。

継承(NSOBjectから継承するために行う)を使用する場合は、スーパークラスがすべてを正しく初期化したことを確認する必要があります。これがsuperの呼び出しです。ランタイムエラーが発生しないことを確認するには、self!= nilを確認します。これは、if(self)を使用して暗黙的に行います。

self.mItems = [self.mItems init]; // doesn't do anything, since you call the getter for mItems with self.mItems and try to init. then you try to set mItmes to itself. 

このコードを使用します:

@implementation TAWChapter

@synthesize mSubject, mItems;

- (id)init
{
    self = [super init];
    if (self) {
        mItems = [[NSMutableArray alloc] init];
    }
    return self;
}

- (void) setSubject:(NSString *)subject{
    mSubject = subject; 
}

- (void) addItem:(NSString *)item{
    [mItems addObject:item]; 
}

@end
6
MJB

superを呼び出し、その結果をselfメソッドのinitに割り当てる必要があります。

- (id)init
{
    self = [super init];
    if (self) {
        self.mItems = [[NSMutableArray alloc] init];
    }
    return self;
}
3
omz

別の方法は、NSArrayからNSMutableArrayを作成することです。

NSMutableArray *myMutableArray = [NSMutableArray arrayWithArray:myArray];
1