Skip to content

Instantly share code, notes, and snippets.

@gfx

gfx/objc-raii.md Secret

Last active December 31, 2015 00:18
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gfx/01bf4c3bcc7c9cf5acbf to your computer and use it in GitHub Desktop.
Save gfx/01bf4c3bcc7c9cf5acbf to your computer and use it in GitHub Desktop.

Objective-C RAII pattern

2013/12/11 #potatotips

FUJI Goro (gfx) at COOKPAD, Inc.

Self Introduction

RAII pattern

Resource Aquisition Is Initialization

  • C++で発明されたリソース管理パターン
  • オブジェクトのライフサイクルとリソース管理をリンクさせる手法
  • Objective-Cでは、-init でリソースを獲得して -dealloc でリソースを開放するとRAIIになる
  • ここでリソースとは「開始と終了のある何か」のこと
    • サブプロセスのforkとkill
    • ネットワークインジケータ(a.k.a. 「くるくる」)のon/off
    • ある区間でかかった時間を測定する

Example 1: NetworkActivityIndicator

NetworkActivityIndicatorをon/offする。

@interface NetworkActivityGuard : NSObject
@end

@implementation NetworkActivityGuard

- (id)init
{
    self = [super init];
    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible: YES];
    return self;
}

- (void)dealloc
{
    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible: NO];
}

@end

Example 2: ScopeGuard

任意のコードをスコープ終了時に実行できる。golangのdefer文のObjC版。

@interface ScopeGuard : NSObject
@end

typedef void (^voidBlock)();

@implementation ScopeGuard {
    voidBlock _block;
}

- (id)initWithBlock: (voidBlock)block
{
    self = [super init];
    self->_block = [block copy];
    return self;
}

- (void)dealloc
{
    _block();
}

+(id)newWitBlock:(voidBlock)block
{
    return [[self alloc] initWithBlock:block];
}

@end // ScopeGuard

Usage of Guard classes

- (IBAction)startAction:(id)sender {
    NSLog(@"started");

    self.label.text = @"Working In Progress";
    
    NSMutableArray * const guards = [NSMutableArray new];
    [guards addObject: [ScopeGuard newWitBlock:^{
        self.label.text = @"Hello, world!";
    }]];
    [guards addObject:[NetworkActivityGuard new]];

    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
    
    double const delayInSeconds = 1.5;
    dispatch_time_t const popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        NSLog(@"finished");
        if (YES /* complex condition */) {
            return; // no need to release guard objects!
        }
        
        (void)guards; // to capture guard objects!
    });
}

NOTE: (void)guard

  • 「guradsを参照する、しかし使わない」と読む
  • ブロックに変数を強制的にキャプチャさせるときのイディオム
  • voidにキャストしているのは、これがないとコンパイラが警告するため

DEMO

(if time permits)

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment