<<iOS 與OS X多線程和內(nèi)存管理>>筆記:Blocks

注:本文為筆記形式,所以很多都是摘抄的.<<iOS 與OS X多線程和內(nèi)存管理>>書中寫的很棒,簡單易懂,建議各位看官自己去看看.


前言


前面兩篇,我們主要說到的是關(guān)于與內(nèi)存管理相關(guān)的技術(shù),接下來,我們深入了解關(guān)于隱形函數(shù)Blocks的相關(guān)技術(shù).其實在華山論劍之淺談iOS的 target - action設(shè)計模式 和 代理模式 以及Bolck中也說過Block相關(guān)的使用方式,不過都略顯粗糙,所以這次結(jié)合著<<iOS 與OS X多線程和內(nèi)存管理>>這本書,來深入了解一下關(guān)于Blocks的相關(guān)技術(shù).


Blocks簡介


在書中說到int,float是C語言的擴充功能,它是一個帶有自動變量的匿名函數(shù).在日常開發(fā)過程中,我覺得與其說Blocks是一個匿名函數(shù),不如說是它是一種數(shù)據(jù)類型更好理解一點,像int,float一樣存儲數(shù)據(jù),不過int,float存儲的是變量,而Blocks存儲的卻是一個函數(shù).

比如我們可以把一個Blocks聲明成一個類的屬性.首先,在ViewController中,我們先給Block變量改一下名稱,我們就可以聲明成ViewController的屬性了.如下所示.

typedef int(^Block)(void);

@interface ViewController : UIViewController

@property(nonatomic,copy)Block block;
@end

然后我們就可以給屬性賦值了.比如我們?nèi)缦逻M行一個簡單的賦值.

    self.block = ^int{ return 1; };

調(diào)用也是相當?shù)暮唵?如下所示.

    self.block();

那么在實際開發(fā)過程中,Blocks主要是用于什么樣的場景呢?或者說Blocks的主要作用是什么呢?在我們實際開發(fā)過程中,Blocks主要用于傳值和回調(diào),這一點類似于OC中代理協(xié)議模式.現(xiàn)在我們舉個例子說明一下它的主要作用.

假設(shè)有兩個控制器,一個是ViewController對象,另外一個是NewViewController對象;其中,NewViewController對象是通過ViewController對象pushViewController推出來的,我們的目的是通過在NewViewController進行操作,然后給ViewController進行傳值操作.示意圖如下所示.

接下來我們一步一步的進行實現(xiàn),首先,我們需要在NewViewController聲明Block屬性,Block的類型是有參數(shù),無返回值類型的.如下所示.

#import <UIKit/UIKit.h>
typedef void(^Block)(int);
@interface NewViewController : UIViewController

@property(nonatomic,copy)Block block;
@end

接下來,我們在ViewController中創(chuàng)建NewViewController對象,然后對Block進行賦值.

    NewViewController *newVC = [[NewViewController alloc]init];
    
    newVC.block = ^(int number){
    
       printf("value = %d",number);
    };

緊接著就是通過pushViewController這方法推出NewViewController控制器頁面.

    [self.navigationController pushViewController:newVC animated:YES];

然后,我們在NewViewController控制器中進行Block的調(diào)用.

    self.block(666);

通過我們調(diào)用,我們就可以在控制臺打印出如下的信息了.

具體可對照查看BlockDemo


Blocks語法


對于Blocks語法,騷棟只想用一句話來表述,那就是能省則省,我們首先看一個最完整的Blocks的寫法,如下圖所示.

我們在這舉一個例子來說明一下.

    ^int(int number){
        return number;
    }

接下來,我們則說一下什么叫能省則省.凡是沒有的部分我們都可以省去,比如我們沒有返回值,我們表示的完整方式如下所示.

    ^ void (int number){
    
        printf(@"value = %d",number);
    }

上面的這個block等同于下面的block表達式.

    ^ (int number){
        printf(@"value = %d",number);
    }

再比如,如果我們參數(shù)也沒有,返回值也沒有的block,用完整的形式如下表示可以如下表示.

   ^ void ( void ){
        printf(@"value = %d",number);
    }

等同于

   ^ {
        printf(@"value = %d",number);
    }

</br>

Blocks類型變量


Blocks簡介中,我們就對Blocks變量作為對象屬性進行了簡單的舉例.其實,我們在實際開發(fā)過程中也是這樣用的,我們一般要先給block改名,這是為什么呢,因為block很長,所以我們給block改名字后減少了我們代碼量,而且形式上更加容易理解.為了比對改名前后的變化,我們舉例來說明一下.

首先我們定義一個沒有改名的block,然后我們對其進行賦值.

    int (^blk)(void);

    blk= ^(void){
    
        return 1;  
    };

然后,我們先給block進行改名操作之后,再進行進行賦值操作.

typedef int(^Block)(void);

然后聲明變量,緊接著進行賦值.

    Block block;
    
    block = ^(void){
        
        return 1;
    };


截獲自動變量值和截獲自動變量


block具有截獲自動變量值的功能,那么什么叫截獲自動變量值呢?我們看下面的示例.

    int i = 10;
    
    void (^blk)(void) = ^{
    
        printf("Value = %d",i);
    };
  
    i = 5;
    blk();

    printf("value = %d",i);

我們看上面的代碼,首先i的初始值為10,然后我們創(chuàng)建了一個隱形函數(shù)block,并在其中打印了i的值,但是這時候,我們并沒有調(diào)用這個隱性函數(shù),當我們把i重新賦值為5之后,緊接著我們調(diào)用上面的隱性函數(shù).那么我們控制器將會打印出處如下所示的結(jié)果.

從而,我們知道,當隱形函數(shù)block進行聲明實現(xiàn)的時候就截獲了i的值為10,所以調(diào)用的時候并不是5,而是10.這就是隱形函數(shù)block的截獲自動變量值.

那么,截獲自動變量又是什么意思呢?主要是指截獲OC中的對象,比如我們截獲一個數(shù)組并給他添加元素.

    NSMutableArray *array = [[NSMutableArray alloc]init];
    
    void (^blk)(void) = ^{
        
        id obj = [[NSObject alloc]init];
        
        [array addObject:obj];
        
    };

問題來了,我們可不可以直接通過block給變量賦值呢?如下所示.

    int i = 10;
    
    void (^blk)(void) = ^{i = 1;};

或者是這樣對對象進行重新賦值操作.

    NSMutableArray *array ;
    
    void (^blk)(void) = ^{
        array = [[NSMutableArray alloc]init];
    };

那么如果你這樣寫了,直接會報編譯錯誤.如下圖所示.

那么這又是怎么回事呢?我們上面不是明明能打印嗎?為什么這樣使用block就會出現(xiàn)問題呢?我們又該如何解決呢?我們接著看下一個模塊.

</br>

__block說明符


上面的問題是怎么出現(xiàn)的呢?在書中是這樣說的,如果用C語言來描述,即是截獲NSMutableArray類的對象用的結(jié)構(gòu)體實例指針,雖然賦值給截獲的自動變量array的操作會產(chǎn)生編譯錯誤,但使用截獲的值卻不會有任何的問題.我們可以總結(jié)為以下的幾句話.

不管是基本數(shù)據(jù)類型的變量還是對象變量.在使用block對其進行操作的時候,我們允許使用變量的值,但是不允許直接給變量進行賦值操作.

那么我們該如何解決這個問題呢?我們只需要在block需要賦值的變量 前面加上一個__block說明符即可.如下所示.

    __block int i = 10;
    void (^blk)(void) = ^{i = 1;};
    __block NSMutableArray *array ;
    
    void (^blk)(void) = ^{
        array = [[NSMutableArray alloc]init];
    };

這樣,就不會再報編譯錯誤了.我們也就完美解決了上述的問題了.

</br>

Blocks循環(huán)引用


Blocks循環(huán)引用是什么原因造成的呢?Blocks循環(huán)引用跟我們前一篇博客中說的自己引用自己的情況差不多.如下圖所示.

具體的代碼示例,我們可以在一個控制器進行演示,整體代碼如下所示.

#import "NewViewController.h"

typedef void(^Block)(void);

@interface NewViewController ()

@property(nonatomic,copy)Block block;
@property(nonatomic,strong)NSString *objString;

@end

@implementation NewViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    self.objString = @"你好";
    
    self.block = ^(){
    
        NSLog(@"%@",self.objString);
    
    };
}

@end

我們首先進行在控制器的延展部分聲明了block屬性對象和一個測試用的字符串屬性對象.

@property(nonatomic,copy)Block block;
@property(nonatomic,strong)NSString *objString;

然后我們在ViewDidLoad方面方法對字符串進行賦值.

    self.objString = @"你好";

緊接著,我們就在block的實現(xiàn)中使用字符串.

    self.block = ^(){
    
        NSLog(@"%@",self.objString);
    };

這時候Xocde會直接拋出一個編譯警告,意思就是存在內(nèi)存泄漏.

那么解決方案也是跟前一篇博客說的那樣,直接使用__weak修飾符.如下所示.

    __weak typeof(self)tmpe =self;
    
    self.block = ^(){
    
        NSLog(@"%@",tmpe.objString);
    };


尾聲


第三個模塊是關(guān)于多線程GCD的API相關(guān)因為以前都做過了,所以接下來,將看另外一本書,這本<<iOS 與OS X多線程和內(nèi)存管理>>就到這了.歡迎大家一起來討論<<iOS 與OS X多線程和內(nèi)存管理>>相關(guān)問題,如果有任何問題,歡迎聯(lián)系騷棟,謝謝.最后還是<<iOS 與OS X多線程和內(nèi)存管理>>的pdf版的下載傳送門,各位看官可以自行去參考查看.

<<iOS 與OS X多線程和內(nèi)存管理>>的pdf版?zhèn)魉烷T??


最后編輯于
?著作權(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)容

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