2012年6月28日

[memo]iOS with Block

雖然現在已經要出iOS6了,不過還是要memo一個在iOS4就推出的功能Block,Block是C Language撰寫的extension for Objective-C, 不過在其他語言類似的功能可能稱作Closure(因為我也不懂所以就不贅述啦 飄~)
白話一點Block就是一個區間(封閉的), 在程式中可以把他當作是一個function(method)既然是function的話為什麼還要特別介紹, 因為要報告它可以不用宣告就使用, 而且有好多API都直接支援了~

接下來就直接看看程式碼
//sample1
[UIView animateWithDuration:2.0
   animations:
    ^ {
        self.view.alpha = 1.0;
        self.view.frame = CGRectMake(176.0, 258.0, 72.0, 96.0);
        self.view.transform = CGAffineTransformMakeRotation(M_PI);
    }
];
//sample2
NSArray *cards = [NSArray arrayWithObjects:@"Jack", @"Queen", @"King", @"Ace", nil];
 
[cards enumerateObjectsUsingBlock:
    ^(id object, NSUInteger index, BOOL *stop) {
       NSLog(@"%@ card at index %d", object, index);
    }
];
//end
上面兩個範例highlight的部分, 就是Block, 如果有注意到的話, 應該會知道這兩個Block不同的地方在於一個有待參數, 一個沒有參數(下方highlight部分)
//block1
^ {
    //statement ...
}
//block2
^(id object, NSUInteger index, BOOL *stop) {
    //statement ...
}
從上面, 可以知道block是可以有待參數跟沒有參數, 接著我們更進一步的介紹Block的成分構成
void   (^BlockName)   (void)
傳回型別 Block的名稱    參數

BlockName();
使用
上面的code, 我們可以把block分成三個部分,分別是 傳回型別(return type)、BlockName跟帶入的參數(parameters), 其中參數如果是void, 在宣告block的時候可以直接把引入的參數省略, 下面就來宣告幾個Block並使用
//sample1
void (^myName)(void) = ^ {
    NSLog(@"Green");
};
myName();

//sample2
int (^getAge)(void) = ^ {
    return 26;
};
int myAge = getAge();

//sample3
NSString* (^getBirthday)(int) = ^(int mode) {
    NSString* birthday;
    switch(mode) {
        case 0:
            birthday = @"75.1.15";
        break;
        default:
            birthday = @"1986.1.15";
    }
    return birthday;
};
NSString* myBirthday = getBirthday(1);
看完前面有一些了解過後,我們再拿UIView有使用Block的Method來相互對照一下
+ (void)animateWithDuration:(NSTimeInterval)duration
              animations:(void (^)(void))animations
在第二行的地方, 並沒有像前面說的有一個Block's Name, 照邊我個人理解是因為他的Block是直接使用, 所以就直接用^(cross)符號來代表匿名的block; 接著我們繼續看下去
//sample1
void (^animation)(void) = ^{
    self.view.alpha = 1.0;
    self.view.frame = CGRectMake(176.0, 258.0, 72.0, 96.0);
    self.view.transform = CGAffineTransformMakeRotation(M_PI);
}
[UIView animateWithDuration:2.0
                 animations:animation
];
//sample2
[UIView animateWithDuration:2.0
   animations:
    ^ {
        self.view.alpha = 1.0;
        self.view.frame = CGRectMake(176.0, 258.0, 72.0, 96.0);
        self.view.transform = CGAffineTransformMakeRotation(M_PI);
    }
];
sample1是用先宣告一個跟UIView ClassMethod 需要的Block Method 一樣的Block animation, 然後在呼叫UIView ClassMethod 並將 block animation 指定過去; 更快直接的話可以像sample2一樣直接打一段匿名的block, 也是可以順利執行的.

接著再來介紹 __block 這個關鍵字, 這個關鍵字是讓在block method外宣告的變數在block method可以當成變數來使用, 前面有提到說Block本身是一個封閉區間, 所以在宣告Block method本身的時候, 他會將block外部的變數當作常數來使用, 所以需要在Block變更Block外部的變數, 只要在宣告的變數前面加上__block就可以了
__block int count = 0;
void(^increase)(void)=^ {
    count++;
    NSLog(@"count:%d", count);
};

increase();// 1
increase();// 2 ...
當我們在coding的時候, 如果有一個mehtod像上面的UIView的ClassMethod用到了Block, 但是如果要帶入的參數很多, 那在閱讀的時候會因為太長就會比較不方便, 我們可用typedef來解決這個問題

下面我建立了一個sampleMethod並要帶入一個block, 接著我們使用typedef來定義type SampleBlock, 接著把method改寫成第6行的樣子; 最下面就是一些簡單的use sample code
//before
-(void)sampleMehtod:(void(^)(void))aBlock;

//after
typedef void(^SampleBlock)(void);
-(void)sampleMethod:(SampleBlock)aBlock;

//use
SampleBlock testBlock = ^ {
    //statement ...
};

[self sampleMethod:testBlock];

到這邊就告一段落啦, 因為打著Memo的口號, 我是用我自己理解的方式來寫可能只有我自己看得比較懂啦
如果有錯誤也請提出, 感謝

沒有留言:

張貼留言