block使用
第一部分
定義和使用Block,
void (^printBlock)() = ^(){
printf("no number");
};
printBlock();
printBlock(9);
int mutiplier = 7;
//(3)定義名為myBlock的代碼塊,返回值類型為int
int (^myBlock)(int) = ^(int num){
return num*mutiplier;
}
//使用定義的myBlock
int newMutiplier = myBlock(3);
printf("newMutiplier is %d",myBlock(3));
定義在-viewDidLoad方法外部
//(2)定義一個(gè)有參數(shù),沒有返回值的Block
void (^printNumBlock)(int) = ^(int num){
printf("int number is %d",num);
};
定義Block變量,就相當(dāng)于定義了一個(gè)函數(shù)。但是區(qū)別也很明顯,因?yàn)楹瘮?shù)肯定是在-viewDidLoad方法外面定義,而Block變量定義在了viewDidLoad方法內(nèi)部。當(dāng)然,我們也可以把Block定義在-viewDidLoad方法外部,例如上面的代碼塊printNumBlock的定義,就在-viewDidLoad外面。
再來看看上面代碼運(yùn)行的順序問題,以第(3)個(gè)myBlock距離來說,在定義的地方,并不會(huì)執(zhí)行Block{}內(nèi)部的代碼,而在myBlock(3)調(diào)用之后才會(huì)執(zhí)行其中的代碼,這跟函數(shù)的理解其實(shí)差不多,就是只要在調(diào)用Block(函數(shù))的時(shí)候才會(huì)執(zhí)行Block體內(nèi)(函數(shù)體內(nèi))的代碼。所以上面的簡(jiǎn)單代碼示例,我可以作出如下的結(jié)論,
(1)在類中,定義一個(gè)Block變量,就像定義一個(gè)函數(shù);
(2)Block可以定義在方法內(nèi)部,也可以定義在方法外部;
(3)只有調(diào)用Block時(shí)候,才會(huì)執(zhí)行其{}體內(nèi)的代碼;
(PS:關(guān)于第(2)條,定義在方法外部的Block,其實(shí)就是文件級(jí)別的全局變量)
那么在類中定義一個(gè)Block,特別是在-viewDidLoad方法體內(nèi)定義一個(gè)Block到底有什么意義呢?我表示這時(shí)候只把它當(dāng)做私有函數(shù)就可以了。我之前說過,Block其實(shí)就相當(dāng)于代理,那么這時(shí)候我該怎樣將其與代理類比以了解呢。這時(shí)候我可以這樣說:本類中的Block就相當(dāng)于類自己服從某個(gè)協(xié)議,然后讓自己代理自己去做某個(gè)事情。
第二部分
__block關(guān)鍵字的使用
在Block的{}體內(nèi),是不可以對(duì)外面的變量進(jìn)行更改的,比如下面的語句,
int x = 100;
void (^sumXAndYBlock)(int) = ^(int y){
x = x+y;
printf("new x value is %d",x);
};
sumXAndYBlock(50);
這段代碼有什么問題呢,Xcode會(huì)提示x變量錯(cuò)誤信息:Variable is not assigning (missing __block type),這時(shí)候給int x = 100;語句前面加上__block關(guān)鍵字即可,如下,
__block int x = 100;
這樣在Block的{}體內(nèi),就可以修改外部變量了。
第三部分:Block作為property屬性實(shí)現(xiàn)頁面之間傳值
第二個(gè)頁面總的代碼如下
@interface NextViewController : UIViewController
@property (nonatomic, copy) void (^NextViewControllerBlock)(NSString *tfText);
@end
//NextViewContorller.m 文件
- (IBAction)popBtnClicked:(id)sender {
if (self.NextViewControllerBlock) {
self.NextViewControllerBlock(self.inputTF.text);
}
[self.navigationController popViewControllerAnimated:YES];
}
第一個(gè)頁面總的代碼如下
- (IBAction)btnClicked:(id)sender {
NextViewController *nextVC = [[NextViewController alloc] initWithNibName:@"NextViewController" bundle:nil];
nextVC.NextViewControllerBlock = ^(NSString *tfText){
[self resetLabel:tfText];
};
[self.navigationController pushViewController:nextVC animated:YES];
}
#pragma mark - NextViewControllerBlock method
- (void)resetLabel:(NSString *)textStr
{
self.nextVCInfoLabel.text = textStr;
}
block使用分析 就像delegate的簡(jiǎn)化版
代理設(shè)計(jì)模式對(duì)于iOS開發(fā)的人來說肯定很熟悉了,代理delegate就是委托另一個(gè)對(duì)象來幫忙完成一件事情,為什么要委托別人來做呢,這其實(shí)是MVC設(shè)計(jì)模式中的模塊分工問題,例如View對(duì)象它只負(fù)責(zé)顯示界面,而不需要進(jìn)行數(shù)據(jù)的管理,數(shù)據(jù)的管理和邏輯是Controller的責(zé)任,所以此時(shí)View就應(yīng)該將這個(gè)功能委托給Controller去實(shí)現(xiàn),當(dāng)然你作為碼農(nóng)強(qiáng)行讓View處理數(shù)據(jù)邏輯的任務(wù),也不是不行,只是這就違背了MVC設(shè)計(jì)模式,項(xiàng)目小還好,隨著功能的擴(kuò)展,我們就會(huì)發(fā)現(xiàn)越寫越難寫;還有一種情況,就是這件事情做不到,只能委托給其他對(duì)象來做了,下面的例子中我會(huì)說明這種情況。
實(shí)際情景使用
下面的代碼我想實(shí)現(xiàn)一個(gè)簡(jiǎn)單的功能,場(chǎng)景描述如下:TableView上面有多個(gè)CustomTableViewCell,cell上面顯示的是文字信息和一個(gè)詳情Button,點(diǎn)擊button以后push到一個(gè)新的頁面。為什么說這個(gè)場(chǎng)景用到了代理delegate?因?yàn)閎utton是在自定義的CustomTableViewCell上面,而cell沒有能力實(shí)現(xiàn)push的功能,因?yàn)閜ush到新頁面的代碼是這樣的,
[self.navigationController pushViewController];
所以這時(shí)候CustomTableViewCell就要委托它所在的Controller去做這件事情了。
為了方便比較 先使用delegate的方式實(shí)現(xiàn)
按照我的編碼習(xí)慣,我喜歡把委托的協(xié)議寫在提出委托申請(qǐng)的類的頭文件里面,現(xiàn)在的場(chǎng)景中是CustomTableViewCell提出了委托申請(qǐng),下面是簡(jiǎn)單的代碼
@protocol CustomCellDelegate <NSObject>
- (void)pushToNewPage;
@end
@interface CustomTableViewCell : UITableViewCell
@property (nonatomic, assign) id<CustomCellDelegate> delegate;
@property (nonatomic, strong) UILabel *text1Label;
@property (nonatomic, strong) UIButton *detailBtn;
@end
上面的代碼在CustomTableViewCell.h中定義了一個(gè)協(xié)議CustomCellDelegate,它有一個(gè)需要實(shí)現(xiàn)的pushToNewPage方法,然后還要寫一個(gè)屬性修飾符為assign、名為delegate的property,之所以使用assign是因?yàn)檫@涉及到內(nèi)存管理的東西,以后的博客中我會(huì)專門說明原因。
接下來在CustomTableViewCell.m中編寫B(tài)utton點(diǎn)擊代碼,
[self.detailBtn addTarget:self action:@selector(btnClicked:) forControlEvents:UIControlEventTouchUpInside];
對(duì)應(yīng)的btnClicked方法如下
- (void)btnClicked:(UIButton *)btn {
if (self.delegate && [self.delegaterespondsToSelector:@selector(pushToNewPage)]) {
[self.delegate pushToNewPage];
}
}
上面代碼中的判斷條件最好是寫上,因?yàn)檫@是判斷self.delegate是否為空,以及實(shí)現(xiàn)CustomCellDelegate協(xié)議的Controller是否也實(shí)現(xiàn)了其中的pushToNewPage方法。
接下來就是受到委托申請(qǐng)的類,這里是對(duì)應(yīng)CustomTableViewCell所在的ViewController,它首先要實(shí)現(xiàn)CustomCellDelegate協(xié)議,然后要實(shí)現(xiàn)其中的pushToNewPage方法,還有一點(diǎn)不能忘記的就是要設(shè)置CustomTableViewCell對(duì)象cell的delegate等于self,很多情況下可能忘了寫cell.delegate = self;導(dǎo)致遇到問題不知云里霧里。下面的關(guān)鍵代碼都是在ViewController.m中,
首先是服從CumtomCellDelegate協(xié)議,這個(gè)大家肯定都知道,就像很多系統(tǒng)的協(xié)議,例如UIAlertViewDelegate、UITextFieldDelegate、UITableViewDelegate、UITableViewDatasource一樣。
@interface ViewController ()<CustomCellDelegate>
@property (nonatomic, strong) NSArray *textArray;
@end
//然后是實(shí)現(xiàn)CustomCellDelegate協(xié)議中的pushToNewPage方法,
- (void)pushToNewPage {
DetailViewController*detailVC = [[DetailViewController alloc] init];
[self.navigationController pushViewController:detailVC animated:YES];
}
//還有一個(gè)步驟最容易被忘記,就是設(shè)置CumtomTableViewCell對(duì)象cell的delegate,如下代碼,
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *simpleIdentify = @"CustomCellIdentify";
CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleIdentify];
if (cell == nil) {
cell = [[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleIdentify];
}
//下面代碼很關(guān)鍵
cell.delegate = self;
cell.text1Label.text = [self.textArray objectAtIndex:indexPath.row];
return cell;
}
通過cell.delegate = self;確保了CustomTableViewCell.m的判斷語句if(self.delegate && ...){}中得self.delegate不為空,此時(shí)的self.delegate其實(shí)就是ViewController,cell對(duì)象委托了ViewController實(shí)現(xiàn)pushToNewPage方法。這個(gè)簡(jiǎn)單的場(chǎng)景描述了使用代理的一種情況,就是CustomTableViewCell沒有能力實(shí)現(xiàn)pushViewController的功能,所以委托ViewController來實(shí)現(xiàn)。
下面使用block的方式實(shí)現(xiàn)
Block是一個(gè)C語言的特性,它就是C語言的函數(shù)指針,在使用中最多的就是進(jìn)行函數(shù)回調(diào)或者事件傳遞,比如發(fā)送數(shù)據(jù)到服務(wù)器,等待服務(wù)器反饋是成功還是失敗,此時(shí)block就派上用場(chǎng)了,這個(gè)功能的實(shí)現(xiàn)也可用使用代理,這么說的話,感覺block是不是有點(diǎn)像代理了呢?
我之前接觸block,都是使用它作為函數(shù)參數(shù),當(dāng)時(shí)感覺不是很理解?,F(xiàn)在在項(xiàng)目中,很多時(shí)候block作為property,這樣更加簡(jiǎn)單直接,想想,其實(shí)property不就是定義的合成存儲(chǔ)的變量嘛,而block作為函數(shù)參數(shù)也是定義的變量,所以作為函數(shù)參數(shù)或者作為property本質(zhì)沒有區(qū)別。
看一看別人總結(jié)的block的語法吧,http://fuckingblocksyntax.com,這個(gè)鏈接亮了,fucking block syntax,操蛋的block語法啊。block有如下幾種使用情況,
1、作為一個(gè)本地變量(local variable)
returnType (^blockName)(parameterTypes) = ^returnType(parameters) {
//blablabla
};
2、作為@property
@property (nonatomic, copy) returnType (^blockName)(parameterTypes);
3、作為方法的參數(shù)(method parameter)
- (void)someMethodThatTakesABlock:(returnType (^)(parameterTypes))blockName;
4、作為方法參數(shù)的時(shí)候被調(diào)用
[someObject someMethodThatTakesABlock: ^returnType (parameters) {...}];
5、使用typedef來定義block,可以事半功倍
typedef returnType (^TypeName)(parameterTypes);
TypeName blockName = ^returnType(parameters) {...};
上面我也只是復(fù)制粘貼了一下,接下來還是實(shí)現(xiàn)點(diǎn)擊CustomTableViewCell上面的Button實(shí)現(xiàn)頁面跳轉(zhuǎn)的功能,我之前不止一次的類比block就像delegate,這邊我也是思維慣性,下面的內(nèi)容我就當(dāng)block為代理,一些用詞描述還是跟delegate差不多。首先,在提出委托申請(qǐng)的CustomTableViewCell中定義block的property,
@interface CustomTableViewCell : UITableViewCell
@property (nonatomic, strong) UILabel *text1Label;
@property (nonatomic, strong) UIButton *detailBtn;
/*delegate的定義 我沒有刪除,因?yàn)榇蠹铱梢灶惐攘丝聪?/
@property (nonatomic, assign) id<CustomCellDelegate> delegate;
/*這里定義了ButtonBlock*/
@property (nonatomic, copy) void (^ButtonBlock)();
@end
這里用copy屬性來修飾ButtonBlock property,這個(gè)原因,我會(huì)在以后的博客中作專門的解釋。
接下來在CustomTableViewCell中給它上面的detailBtn綁定點(diǎn)擊方法,
[self.detailBtn addTarget:self action:@selector(btnClicked:) forControlEvents:UIControlEventTouchUpInside];
- (void)btnClicked:(UIButton *)btn {
//這是之前的delegate
if (self.delegate && [self.delegate respondsToSelector:@selector(pushToNewPage)]) {
[self.delegate pushToNewPage];
}
//這是現(xiàn)在我們要說的block
if (ButtonBlock) {
ButtonBlock();
}
}
下面是一個(gè)關(guān)鍵性的地方,在ViewController2中設(shè)置其CustomTableViewCell的cell對(duì)象的ButtonBlock,也就是給它賦值,此處我還是保留了cell.delegate = self; ##代碼##
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *blockIdentify = @"BlockIdentify";
CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:blockIdentify];
if (cell == nil) {
cell = [[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:blockIdentify];
}
cell.text1Label.text = [self.textArray objectAtIndex:indexPath.row];
//delegate的不可缺少的代碼,這里放在這兒只是為了給各位類比一下
cell.delegate = self;
//ButtonBlock不可缺少的代碼
cell.ButtonBlock = ^{
[self pushToNewPage2];
};
return cell;
}
之所以cell.ButtonBlock = ^{};賦值,是因?yàn)槲覀兾覀兪沁@樣定義ButtonBlock的,void (^ButtonBLock)(),表示無返回值無參數(shù)。
你們看這個(gè)方法是不是與CustomCellDelegate協(xié)議中的pushToNewPage方法類似。然后在回過頭來類比一樣,是不是block就是精簡(jiǎn)版的delegate,因?yàn)閐elegate設(shè)計(jì)模式要寫協(xié)議CustomCellDelegate,還有容易遺漏cell.delegate = self;但是block使用的時(shí)候就簡(jiǎn)單多了。