[轉(zhuǎn)]iOS中Block全面分析

本文轉(zhuǎn)載自:http://my.oschina.net/leejan97/blog/268536

本文翻譯自蘋果的文檔,有刪減,也有添加自己的理解部分。

如果有Block語法不懂的,可以參考fuckingblocksyntax。

為了方便對比,下面的代碼我假設(shè)是寫在ViewController子類中的。

1、定義和使用Block

- (void)viewDidLoad  
{  
    [super viewDidLoad];  
    //(1)定義無參無返回值的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)定義一個有參數(shù),沒有返回值的Block  
void (^printNumBlock)(int) = ^(int num){  
    printf("int number is %d",num);  
};  

定義Block變量,就相當于定義了一個函數(shù)。但是區(qū)別也很明顯,因為函數(shù)肯定是在-viewDidLoad方法外面定義,而Block變量定義在了viewDidLoad方法內(nèi)部。當然,我們也可以把Block定義在-viewDidLoad方法外部,例如上面的代碼塊printNumBlock的定義,就在-viewDidLoad外面。

再來看看上面代碼運行的順序問題,以第(3)個myBlock距離來說,在定義的地方,并不會執(zhí)行Block{}內(nèi)部的代碼,而在myBlock(3)調(diào)用之后才會執(zhí)行其中的代碼,這跟函數(shù)的理解其實差不多,就是只要在調(diào)用Block(函數(shù))的時候才會執(zhí)行Block體內(nèi)(函數(shù)體內(nèi))的代碼。所以上面的簡單代碼示例,我可以作出如下的結(jié)論:

  1. 在類中,定義一個Block變量,就像定義一個函數(shù);
  2. Block可以定義在方法內(nèi)部,也可以定義在方法外部。
  3. 只有調(diào)用Block時候,才會執(zhí)行其{ }體內(nèi)的代碼;(PS:關(guān)于第(2)條,定義在方法外部的Block,其實就是文件級別的全局變量)

那么在類中定義一個Block,特別是在-viewDidLoad方法體內(nèi)定義一個Block到底有什么意義呢?我表示這時候只把它當做私有函數(shù)就可以了。我之前說過,Block其實就相當于代理,那么這時候我該怎樣將其與代理類比以了解呢。這時候我可以這樣說:本類中的Block就相當于類自己服從某個協(xié)議,然后讓自己代理自己去做某個事情。很拗口吧?看看下面的代碼:

//定義一個協(xié)議  
@protocol ViewControllerDelegate<NSObject>  
- (void)selfDelegateMethod;  
@end  
   
//本類實現(xiàn)這個協(xié)議ViewControllerDelegate  
@interface ViewController ()<ViewControllerDelegate>  
@property (nonatomic, assign) id<ViewControllerDelegate> delegate;  
   
@end  

接著在-viewDidLoad中的代碼如下:

- (void)viewDidLoad  
{  
    [super viewDidLoad];  
    // Do any additional setup after loading the view from its nib.  
    self.delegate = self;  
    if (self.delegate && [self.delegate respondsToSelector:@selector(selfDelegateMethod)]) {  
        [self.delegate selfDelegateMethod];  
    }  
}  
   
#pragma mark - ViewControllerDelegate method  
//實現(xiàn)協(xié)議中的方法  
- (void)selfDelegateMethod  
{  
    NSLog(@"自己委托自己實現(xiàn)的方法");  
} 

看出這種寫法的奇葩地方了嗎?自己委托自己去實現(xiàn)某個方法,而不是委托別的類去實現(xiàn)某個方法。本類中定義的一個Block其實就是閑的蛋疼,委托自己去字做某件事情,實際的意義不大,所以你很少看見別人的代碼直接在類中定義Block然后使用的,Block很多的用處是跨越兩個類來使用的,比如作為property屬性或者作為方法的參數(shù),這樣就能跨越兩個類了。

2、__block關(guān)鍵字的使用

在Block的{ }體內(nèi),是不可以對外面的變量進行更改的,比如下面的語句:

- (void)viewDidLoad  
{  
    //將Block定義在方法內(nèi)部  
    int x = 100;  
    void (^sumXAndYBlock)(int) = ^(int y){  
    x = x+y;  
    printf("new x value is %d",x);  
    };  
    sumXAndYBlock(50);  
}  

這段代碼有什么問題呢,Xcode會提示x變量錯誤信息:Variable is not assigning (missing __block type),這時候給int x = 100;語句前面加上__block關(guān)鍵字即可(注意前面有兩個短杠),如下:

__block int x = 100;

這樣在Block的{ }體內(nèi),就可以修改外部變量了。

3、第三部分:Block作為property屬性實現(xiàn)頁面之間傳值

需求:在ViewController中,點擊Button,push到下一個頁面NextViewController,在NextViewController的輸入框TextField中輸入一串字符,返回的時候,在ViewController的Label上面顯示文字內(nèi)容。

(1)第一種方法:首先看看通過“協(xié)議/代理”是怎么實現(xiàn)兩個頁面之間傳值的吧

//NextViewController是push進入的第二個頁面  
//NextViewController.h 文件  
//定義一個協(xié)議,前一個頁面ViewController要服從該協(xié)議,并且實現(xiàn)協(xié)議中的方法  
@protocol NextViewControllerDelegate <NSObject>  
- (void)passTextValue:(NSString *)tfText;  
@end  
   
@interface NextViewController : UIViewController  
@property (nonatomic, assign) id<NextViewControllerDelegate> delegate;  
   
@end  
   
//NextViewController.m 文件  
//點擊Button返回前一個ViewController頁面  
- (IBAction)popBtnClicked:(id)sender {  
    if (self.delegate && [self.delegate respondsToSelector:@selector(passTextValue:)]) {  
        //self.inputTF是該頁面中的TextField輸入框  
        [self.delegate passTextValue:self.inputTF.text];  
    }  
    [self.navigationController popViewControllerAnimated:YES];  
} 

接下來我們在看看ViewController文件中的內(nèi)容:

//ViewController.m 文件  
@interface ViewController ()<NextViewControllerDelegate>  
@property (strong, nonatomic) IBOutlet UILabel *nextVCInfoLabel;  
   
@end  
//點擊Button進入下一個NextViewController頁面  
- (IBAction)btnClicked:(id)sender  
{  
    NextViewController *nextVC = [[NextViewController alloc] initWithNibName:@"NextViewController" bundle:nil];  
    nextVC.delegate = self;//設(shè)置代理  
    [self.navigationController pushViewController:nextVC animated:YES];  
}  
   
//實現(xiàn)協(xié)議NextViewControllerDelegate中的方法  
#pragma mark - NextViewControllerDelegate method  
- (void)passTextValue:(NSString *)tfText  
{  
    //self.nextVCInfoLabel是顯示NextViewController傳遞過來的字符串Label對象  
    self.nextVCInfoLabel.text = tfText;  
}  

這是通過“協(xié)議/代理”來實現(xiàn)的兩個頁面之間傳值的方式。

(2)第二種方法:使用Block作為property,實現(xiàn)兩個頁面之間傳值

先看看NextViewController文件中的內(nèi)容:

//NextViewController.h 文件  
@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];  
}  

再來看看ViewController文件中的內(nèi)容:

- (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來實現(xiàn)兩個頁面之間傳值的目的,實際上就是取代了Delegate的功能。

另外,博客中的代碼Sample Code可以在Github下載,如果因為Github被墻了,可以在終端使用git clone + 完整鏈接,即可克隆項目到本地。

Github中的代碼,可以開啟兩種調(diào)試模式,你需要在項目的配置文件BlockSamp-Prefix.pch中注釋或者解注釋下面的代碼:

#define Debug_BlcokPassValueEnable  

即可開啟兩種調(diào)試的方式,如果注釋了上面的語句就是使用Delegate進行調(diào)試;否則使用Block進行調(diào)試。


查看作者首頁

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

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

  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,628評論 30 472
  • 總結(jié): block本質(zhì)是指向一個結(jié)構(gòu)體的一個指針運行時機制 比較高級的特性 純C語言,平時寫的OC代碼 轉(zhuǎn)換成C語...
    JonesCxy閱讀 423評論 5 0
  • 把網(wǎng)上的一些結(jié)合自己面試時遇到的面試題總結(jié)了一下,以后有新的還會再加進來。 1. OC 的理解與特性 OC 作為一...
    AlaricMurray閱讀 2,669評論 0 20
  • 原文地址:iOS應(yīng)用架構(gòu)談 view層的組織和調(diào)用方案 當我們開始設(shè)計View層的架構(gòu)時,往往是這個App還沒有開...
    Jabir_Zhang閱讀 994評論 0 1
  • 普希金 我的名字對你有什么意義? 它會死去, 像大海拍擊海堤, 發(fā)出的憂郁的汩汩濤聲, 像密林中幽幽的夜聲。 它會...
    wi1l閱讀 153評論 0 0

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