Hatena::Groupiphone-dev

iPhoneアプリ開発まっしぐら★ このページをアンテナに追加 RSSフィード

引っ越し後の日記はコチラです

tokoromのその他の日記

2010-03-14

プロパティ解放の記述方法についての提案

| 03:25 | はてなブックマーク -  プロパティ解放の記述方法についての提案 - iPhoneアプリ開発まっしぐら★

例えば、↓のクラスの dealloc で、各プロパティを解放する場合、

@interface Human : NSObject
{
 @private
  NSString* name_; 
  id brain_;
  NSArray* hands_;
  NSArray* foots_;
}

@property (nonatomic, copy) NSString* name;
@property (nonatomic, assign) id brain;
@property (nonatomic, retain) NSArray* hands;
@property (nonatomic, retain) NSArray* foots;

@end

各所でよくみるコードとしては、↓になっていないでしょうか。

◆改善前

- (void)dealloc {
  [name_ release];
  [hands_ release];
  [foots_ release];
  [super dealloc];
}

もちろんこれで問題はありません。

ただ、最近は↓のほうがメリットがあるんじゃないかと考えてます。

◇改善後

- (void)dealloc {
  self.name = nil; 
  self.brain = nil; 
  self.hands = nil; 
  self.foots = nil; 
  [super dealloc];
}

メリットについて

というのも、◇改善後の場合、assign属性のプロパティであっても同じコードで良いからです。

今回のサンプルでいうと、brainプロパティをassign属性としているので、誤って◆改善前のコードで

  [brain_ release];

としてしまったら、解放してはいけないものなのを解放することになってしまいます。対して◇改善後のコードで

  self.brain = nil; 

となっているのは、アクセサの中で単なるnil代入として扱かってくれるため問題ありません。

こうすることで、開発中にプロパティの属性をretainに変えようが、assignだろうがcopyだろうがdeallocメソッドは修正しなくてよくなります。

私はこれを忘れて不正アクセスやメモリリークが発生することがよくありましたが、◇改善後のコードに統一することで皆無となりました。

※もし、ここのassignでもretainでもプロパティへのnil代入で問題ないよ、という辺りがよくわからんという場合には、こちらに補足記事を書かせていただきましたので、よろしければ見てみてください。


他にも、deallocでない部分で、例えばfinalizeメソッドみたいなものを自分で作った場合、

- (void)finalize {
  [hands_ release];
  [foots_ release];
}

↑だとreleaseにより参照カウンタが0となって実体が解放されたとしてもnil代入されないため、その後にもう1度このメソッドが呼ばれると解放済みのアドレスへのアクセスが発生するためプログラムは不正終了してしまいます。

対して

- (void)finalize {
  self.hands = nil; 
  self.foots = nil; 
}

としておけば、解放後に呼ばれたとしても、解放後はただのnil代入扱いになるので問題ありません。


パフォーマンスが落ちたりしない?

ということで、◇改善後のほうが保守性に優れているのは一目瞭然ですが、ひとつ心配なのは、これにより実行時のパフォーマンスが落ちてしまうことです。

それを調査するためにそれぞれ◆改善前のコードと◇改善後のコードで、今回サンプルとして挙げているHumanクラスのallocとreleaseを10万回繰り返して速度を計測してみました。

  for ( int i = 0; i < 100000; ++i ){
    Human* human = [[Human alloc] init];
    human.brain = self;
    [human release];
  }

結果は以下のとおりです。

試行回数◆改善前◇改善後release1回あたりの差異
1回目3.138秒3.228秒約0.001ミリ秒
2回目3.154秒3.301秒約0.001ミリ秒
3回目3.198秒3.328秒約0.001ミリ秒

※iPhone 3GS (OS 3.1.3) で計測


10万回繰り返してこの差であれば、通常時は◇改善後のやりかたのほうが良いと思います。

この差が問題になるくらいの箇所であれば、きっとC言語で書いていることでしょう。