モダンなObjective-Cプログラミング

齊藤 潤 (@ST63Jun_)

自己紹介

Summary

はじめに

NSNumberリテラル

void main(int argc, const char *argv[]) {
  // character literals.
  NSNumber *theLetterZ = @'Z';          // equivalent to [NSNumber numberWithChar:'Z']

  // integral literals.
  NSNumber *fortyTwo = @42;             // equivalent to [NSNumber numberWithInt:42]
  NSNumber *fortyTwoUnsigned = @42U;    // equivalent to [NSNumber numberWithUnsignedInt:42U]
  NSNumber *fortyTwoLong = @42L;        // equivalent to [NSNumber numberWithLong:42L]
  NSNumber *fortyTwoLongLong = @42LL;   // equivalent to [NSNumber numberWithLongLong:42LL]

  // floating point literals.
  NSNumber *piFloat = @3.141592654F;    // equivalent to [NSNumber numberWithFloat:3.141592654F]
  NSNumber *piDouble = @3.1415926535;   // equivalent to [NSNumber numberWithDouble:3.1415926535]

  // BOOL literals.
  NSNumber *yesNumber = @YES;           // equivalent to [NSNumber numberWithBool:YES]
  NSNumber *noNumber = @NO;             // equivalent to [NSNumber numberWithBool:NO]

#ifdef __cplusplus
  NSNumber *trueNumber = @true;         // equivalent to [NSNumber numberWithBool:(BOOL)true]
  NSNumber *falseNumber = @false;       // equivalent to [NSNumber numberWithBool:(BOOL)false]
#endif
}

Boxed Expression

// numbers.
NSNumber *smallestInt = @(-INT_MAX - 1);  // [NSNumber numberWithInt:(-INT_MAX - 1)]
NSNumber *piOverTwo = @(M_PI / 2);        // [NSNumber numberWithDouble:(M_PI / 2)]

// enumerated types.
typedef enum { Red, Green, Blue } Color;
NSNumber *favoriteColor = @(Green);       // [NSNumber numberWithInt:((int)Green)]

Boxed Expression

// strings.
NSString *path = @(getenv("PATH"));       // [NSString stringWithUTF8String:(getenv("PATH"))]
NSArray *pathComponents = [path componentsSeparatedByString:@":"];

NSArray・NSDictionaryリテラル

NSArray *elements = @[ @"H", @"He", @"O", @"C" ];
NSDictionary *masses = @{ @"H" : @1.0078,  @"He" : @4.0026, @"O" : @15.9990, @"C" : @12.0096 };

NSArray・NSDictionaryの添字アクセス

NSMutableArray *array = ...;
NSUInteger idx = ...;
id newObject = ...;
id oldObject = array[idx];      // -> [array objectAtIndexSubscript:idx]
array[idx] = newObject;         // replace oldObject with newObject

NSMutableDictionary *dictionary = ...;
NSString *key = ...;
oldObject = dictionary[key];    // -> [dictionary objectForKeyedSubscript]
dictionary[key] = newObject;    // replace oldObject with newObject

ARC (Automatic Reference Counting)

ARC (Automatic Reference Counting)

{
    id obj = [NSArray alloc] init];
}

ARCが有効の場合、コンパイラが次のように書き換える

{
    id __strong obj = [NSArray alloc] init];
    [obj release];  // 変数のスコープの範囲を超えるときにreleaseされる
}

ARCを有効にするとどうなるの

以下の制限が発生します:

- (void)dealloc {
     // よくあるdeallocをオーバーライドするイディオム
     [myObject release];
     [super dealloc]; // <- ARC有効の場合はコンパイルエラーになるので消す必要がある
}

ARCの注意点

コードによってはメモリリークが起こる (例:循環参照するオブジェクトの場合)

@interface MyObject : NSObject
{
    id __strong mObj;
}
- (void)setObject:(id __strong)obj;
@end
@implementation MyObject
- (id)init
{
    self = [super init];
    return self;
}
- (void)setObject:(id __strong)obj
{
    mObj = obj; // このときコンパイラは obj をretain する
}
@end
{
    id obj = [[MyObject alloc] init]; // -> obj.retainCount -> 1
    [obj setObject:obj];              // -> obj.retainCount -> 2
}
// スコープ終了によりコンパイラが [obj release]
// obj.release -> 1
// まだretainCountが残っている -> メモリリーク!

これは__weak修飾子を使うことで回避できる

__weak修飾子

@interface MyObject : NSObject
{
    id __weak mObj; // __weak修飾子を指定
}
- (void)setObject:(id __strong)obj;
@end
@implementation MyObject
- (id)init
{
    self = [super init];
    return self;
}
- (void)setObject:(id __strong)obj
{
    mObj = obj; // mObjは__weak修飾付きなので、コンパイラは obj を retain *しない*
}
@end
{
    id obj = [[MyObject alloc] init]; // -> obj.retainCount -> 1
    [obj setObject:obj];              // -> obj.retainCount -> 1
}
// スコープ終了によりコンパイラが [obj release]
// obj.release -> 0
// [obj dealloc]が呼ばれてメモリ解放 -> All right!

ARCのまとめ

Blocks

Blocks以前

@interface Worker : NSObject
@end
@implementation Worker
// 処理終了後にコールバックするメソッド
- (void)somenthingToDo:(id)target delegate:(id)delegate {
    [target ...]; // 何かする
    [delegate didFinishedProcess];
}
@end

// コールバック用のメソッドをわざわざ作る必要がある
- (void)didFinishedProcess {
    NSLog(@"Finished!");
}

- (void)test {
    Worker *worker = [[Worker alloc] init];
    [worker somethingToDo:target delegate:self];
}

Blocks以降

@interface Worker : NSObject
@end
@implementation Worker
// Blockを使うように変更
- (void)somenthingToDo:(id)target finished:(^callback)(void) {
    [target ...]; // 何かする
    callback();
}
@end
- (void)test {
    Worker *worker = [[Worker alloc] init];
    [worker somethingToDo:target finished:^ {
        NSLog(@"Finished!");  // 自然に記述できる!
    }];
}

コーディング量が減ったのがわかったと思います

Blcokの構文

int multiplier = 7;
int (^myBlock)(int) = ^(int num) {
    return num * multiplier;
};

http://bit.ly/ZXZjHR より

関数ポインタとの比較

int func(int num) {
    return num * 7;
}

int main() {
    int multiplier = 7;

    // 関数ポインタ
    int (*myFuncPtr)(int) = &func;
    // Block (関数ポインタの宣言と似てる)
    int (^myBlock)(int) = ^(int num) {
       return num * multiplier;
    };

    (*myFuncPtr)(2); // -> 14
    myBlock(2);      // -> 14
}

自動変数のキャプチャ

int multiplier = 7; // <- この時点でのmultiplierの値がBlockキャプチャされる
int (^myBlock)(int) = ^(int num) {
    return num * multiplier;  // <- multiplierの値は7
};
int multiplier = 7; // <- この時点でのmultiplierの値がBlockキャプチャされる
int (^myBlock)(int) = ^(int num) {
    multiplier = 100;         // <- コンパイルエラー
    return num * multiplier;
};
__block int multiplier = 7; // __block指定子を付ける
int (^myBlock)(int) = ^(int num) {
    multiplier = 100;
    return num * multiplier;
};

NSLog(@"%d", multiplier); // -> 100

GCD (Grand Central Dispatch)

Dispatch Queue

// Serial Dispatch Queueを作成
dispatch_queue_t sdq = dispatch_queue_create(
    "com.example.app.MySerialDispatchQueue", NULL);

// Concurrent Dispatch Queueを作成
dispatch_queue_t cdq = dispatch_queue_create(
    "com.example.app.MyConcurrentDispatchQueue", DISPATCH_QUEUE_CONCURRENT);

// iOS 6未満ではDispatch Queueは手動でリリースする必要がある
dispatch_release(sdq);
dispatch_release(cdq);

// システムが使用するDispath Queueを取得 (RunLoopの処理に使われている)
dispatch_queue_t mdq = dispatch_get_main_queue();

// アプリケーション共用のデフォルトDispath Queueを取得
dispatch_queue_t gdq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 標準の優先度のキューを取得

処理の追加

dispatch_queue_t gdq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(gdq, ^{
    // 並列実行する処理
});
dispatch_async(gdq, ^{
    // 一度に並列実行する数はカーネルが決定する
});

CocoaPods

Podsfile

    platform :ios
    pod 'Sparrow-Framework', '~> 1.4'

まとめ

おわり