Objective-C初心者がやりがちなミスやバッドノウハウまとめ

Java経験者やスクリプト言語経験者が陥りやすい問題を紹介します。

配列型や辞書型にnilを渡してしまう

いずれも例外またはクラッシュが発生してしまいます。

NSString *str = nil;

NSArray *ary = @[str];
NSDictionary *dic = @{@"key": str};

必要に応じてデフォルト値/代替値を指定してあげるとよいでしょう。

NSArray *ary = @[str ?: @"default"];
NSDictionary *dic = @{@"key": str ?: @"default"};
Clangコンパイラでは条件演算子の省略記法を活用できます(cond ?: default)。condの値が偽の場合にはdefaultの値が返されます。真の場合はcondの値そのものが返されます。

nilチェックをしてしまう

Objective-Cのオブジェクトはnilに対するメッセージ呼び出しやプロパティ呼び出しが行えますので、Javaの世界でいうNullPointerException(通称:ヌルポ)の心配はありません。

nilが代入されたオブジェクトのメソッド呼び出しは無視され、nilと同等の結果を返します

そのため、場合によっては以下のようなシンプルな記述が可能となります。

// Before
if (string != nil && 0 < string.length) {
   [list addObject:string];
}
// After
if (0 < string.length) {
   [list addObject:string];
}

NSNumberへの数値変換にメソッドを用いてしまう

イニシャライザやコンビニエンスコンストラクタによる初期化は不要です。@(式)形式による変換が可能ですので、そちらを利用しましょう。

// イニシャライザやコンビニエンスコンストラクタによる変換
NSUInteger a = 99;
NSNumber *num = [NSNumber.alloc initWithInteger:a];
BOOL b = YES;
NSNumber *nun = [NSNumber numberWithBool:b];

// Boxed Expressionsによる変換
NSUInteger a = 99;
BOOL b = YES;
NSNumber *num = @(a);
NSNumber *nun = @(b);

同記法を用いれば、与えられた式の型に応じたコンビニエンスコンストラクタが呼び出されるようになります。上記の例では、@(b)[NSNumber numberWithBool:b]に置き換わります。

グローバル変数をインスタンス変数と勘違いしてしまう

以下のint globalの宣言はインスタンス変数ではなく、グローバル変数です。インスタンス変数は{}内に宣言する必要があります。

@implementation Object {
   int local;   // インスタンス変数
}
int global = 0; // グローバル変数

- (int)instanceMethod { return ++local;  }
+ (int)clazzMethod    { return ++global; }
@end

int main() {
   [Object clazzMethod]; // 1
   [Object clazzMethod]; // 2
   [Object.new instanceMethod]; // 1
   [Object.new instanceMethod]; // 1
}

広告