2012年12月2日

Objective-C的block

iOS4之後,Obj-C多了一個block的東西
最近在玩node.js跟android SDK的時候,也發現有這種類似的寫法
所以就花了一點時間,研讀前輩的文章水果官方技術文件
然後整理出一些心得...

這篇文章在一開始,會先介紹block的宣告與使用方式
接下來,將會用兩個例子,把block當成物件丟到某個method裡面 (類似匿名function的概念)
最後,再用兩個例子來實驗block的性質:可存取block外部的變數
這篇文章暫時不講記憶體管理的部份...




block宣告與使用方式
-(void)blockSample1 {
    NSLog(@"exec blockSample1 method");
    // 第一種方式
    // 宣告一個myBlock型態為: 傳入兩個int, 回傳一個NSString*
    typedef NSString* (^myBlock)(int, int);
    
    // 類似int a; 或是float b;
    myBlock block1;
    
    // 實作block1的內容
    block1 = ^(int x, int y) {
        NSLog(@"[BLOCK1] running");
        return [NSString stringWithFormat:@"%d * %d = %d", x, y, x*y];
    };
    
    
    // 第二種方式
    // 不事先定義type,直接實作它
    // block為: 傳入兩個int, 回傳一個NSString*
    NSString* (^block2)(int, int) = ^(int m, int n) {
        NSLog(@"[BLOCK2] running");
        return [NSString stringWithFormat:@"%d * %d = %d", m, n, m*n];
    };
    
    
    // 使用block
    NSLog(@"block1 returned: [%@]", block1(2,3));
    NSLog(@"block2 returned: [%@]", block2(6,7));
}

執行結果如下:
2012-12-03 15:30:32.621 BlockTest[21129:303] exec blockSample1 method
2012-12-03 15:30:32.622 BlockTest[21129:303] [BLOCK1] running
2012-12-03 15:30:32.622 BlockTest[21129:303] block1 returned: [2 * 3 = 6]
2012-12-03 15:30:32.622 BlockTest[21129:303] [BLOCK2] running
2012-12-03 15:30:32.623 BlockTest[21129:303] block2 returned: [6 * 7 = 42]


把block丟到某個method裡面執行
例子A:
此method傳入一個block變數, 這個block有兩個int傳入, 一個int回傳
下面是method的實作內容
-(void)blockSample2:(int(^)(int, int))aBlock {
    NSLog(@"exec blockSample2 method");
    int a = 5;
    int b = 3;

    // 使用block
    // 傳入5,3, 回傳一個int
    // 不管block裡面的實作。
    int result = aBlock(5, 3);
    NSLog(@"aBlock(%d, %d) result: [%d]", a, b, result);
}

在呼叫此method時,可以直接把block實作出來,然後當做一個物件傳進去
    // 傳入一個block的實作到blockSample2 method
    // blockSample2的宣告如下
    // -(void)blockSample2:(int(^)(int, int))aBlock;
    [obj blockSample2:^int(int a, int b) {
        NSLog(@"[BLOCK] exec block...");
        NSLog(@"[BLOCK] find a:%d, b:%d", a, b);
        return a-b;
    }];
執行結果如下:
2012-12-03 15:30:32.624 BlockTest[21129:303] exec blockSample2 method
2012-12-03 15:30:32.624 BlockTest[21129:303] [BLOCK] exec block...
2012-12-03 15:30:32.625 BlockTest[21129:303] [BLOCK] find a:5, b:3
2012-12-03 15:30:32.625 BlockTest[21129:303] aBlock(5, 3) result: [2]

例子B:
此method傳入一個block變數, 這個block沒有任何傳入跟回傳值
下面是method的實作內容
-(void)blockSample3:(void(^)(void))aBlock {
    NSLog(@"exec blockSample3 method");
    aBlock();
}

在呼叫此method時,可以直接把block實作出來,然後當做一個物件傳進去
    // 傳入一個block的實作到blockSample3 method
    // block型態:無傳入值,無回傳值
    // 此時,block實作可以簡寫成 ^{}
    // blockSample3的宣告如下
    // -(void)blockSample3:(void(^)(void))aBlock;
    [obj blockSample3:^{
        NSLog(@"[BLOCK] exec block...");
    }];
執行結果如下:
2012-12-03 15:30:32.626 BlockTest[21129:303] exec blockSample3 method
2012-12-03 15:30:32.626 BlockTest[21129:303] [BLOCK] exec block...
這種用法以後會特別常見,尤其在GCD...
另外,現在新的presentViewController:animated:completion:方法,也使用block來處理callback了
下面是method的宣告
- (void)presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^)(void))completion;

block可讀取外部變數的性質
直接看code吧...
-(void)blockSample4 {
    NSLog(@"exec blockSample4 method");
    int out_of_block_var = 10;
    
    // 實作 block
    NSString* (^block)(int) = ^(int block_local_var) {
        NSLog(@"[BLOCK] running");
        
        // 在block內可讀取block外的變數,但是是唯讀的
        NSLog(@"[BLOCK] find out var %d, in var %d", out_of_block_var, block_local_var);
        return [NSString stringWithFormat:@"%d - %d = %d",
                out_of_block_var, block_local_var, out_of_block_var - block_local_var];
    };
    
    // 使用 block
    NSLog(@"block2 returned: [%@]", block(8));
}

// block可寫入外部變數,但是要加上__block
// 感覺有點像java的final
-(void)blockSample5 {
    NSLog(@"exec blockSample5 method");
    __block int out_of_block_var = 10;
    NSLog(@"out_of_block_var: %d", out_of_block_var);

    // 實作 block
    NSString* (^block)(int) = ^(int block_local_var) {
        NSLog(@"[BLOCK] running");
        
        NSLog(@"[BLOCK] find out var %d, in var %d", out_of_block_var, block_local_var);
        
        // 將外部變數改成 8 (寫入8到外部變數)
        NSLog(@"[BLOCK] modify out var to \"8\"");
        out_of_block_var = 8;
        
        NSLog(@"[BLOCK] find out var %d, in var %d", out_of_block_var, block_local_var);
        return [NSString stringWithFormat:@"%d - %d = %d",
                out_of_block_var, block_local_var, out_of_block_var - block_local_var];
    };
    
    // 使用 block
    NSLog(@"block2 returned: [%@]", block(8));
    NSLog(@"out_of_block_var: %d", out_of_block_var);
}

執行結果如下:
2012-12-03 15:30:32.627 BlockTest[21129:303] exec blockSample4 method
2012-12-03 15:30:32.628 BlockTest[21129:303] [BLOCK] running
2012-12-03 15:30:32.628 BlockTest[21129:303] [BLOCK] find out var 10, in var 8
2012-12-03 15:30:32.628 BlockTest[21129:303] block2 returned: [10 - 8 = 2]

2012-12-03 15:30:32.629 BlockTest[21129:303] exec blockSample5 method
2012-12-03 15:30:32.630 BlockTest[21129:303] out_of_block_var: 10
2012-12-03 15:30:32.630 BlockTest[21129:303] [BLOCK] running
2012-12-03 15:30:32.630 BlockTest[21129:303] [BLOCK] find out var 10, in var 8
2012-12-03 15:30:32.631 BlockTest[21129:303] [BLOCK] modify out var to "8"
2012-12-03 15:30:32.631 BlockTest[21129:303] [BLOCK] find out var 8, in var 8
2012-12-03 15:30:32.631 BlockTest[21129:303] block2 returned: [8 - 8 = 0]
2012-12-03 15:30:32.632 BlockTest[21129:303] out_of_block_var: 8


One more thing...
這東西我有放在GitHub
有需要的請自取



Block在iOS的殺手及應用,GCD

作者外出取材,等我有空再寫...XD 讓我富姦一下,科科~
(富姦:躺著也中槍)

沒有留言:

張貼留言