OC-Block使用和分析

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)單多了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • *面試心聲:其實(shí)這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,622評(píng)論 30 472
  • 在介紹Block之前通過一個(gè)簡(jiǎn)單的應(yīng)用場(chǎng)景認(rèn)識(shí)下Block 場(chǎng)景描述如下:TableView上面有多個(gè)Custom...
    黑_白_灰閱讀 1,476評(píng)論 4 29
  • iOS網(wǎng)絡(luò)架構(gòu)討論梳理整理中。。。 其實(shí)如果沒有APIManager這一層是沒法使用delegate的,畢竟多個(gè)單...
    yhtang閱讀 5,490評(píng)論 1 23
  • 前段時(shí)間萬達(dá)的《萬達(dá)工作法》引起了很多爭(zhēng)議,早上8點(diǎn)半上班,穿正裝,開會(huì)不準(zhǔn)遲到,而且下級(jí)要比上級(jí)提前5-10分鐘...
    產(chǎn)品狗看世界閱讀 645評(píng)論 0 0
  • 感恩父母賜予我生命,辛苦將我養(yǎng)育! 感恩我家婆的付出! 感恩顧客的關(guān)照! 感恩身體細(xì)胞的和諧相處! 感恩錢寶寶的陪...
    彭焱娟閱讀 123評(píng)論 0 0

友情鏈接更多精彩內(nèi)容