block的使用淺析

1 block的基本概念

1.1 block的產(chǎn)生和用途

代碼塊Block是蘋果在iOS4開始引入的對C語言的擴(kuò)展,用來實(shí)現(xiàn)匿名函數(shù)的特性,Block是一種特殊的數(shù)據(jù)類型,其可以正常定義變量、作為參數(shù)、作為返回值,特殊地,Block還可以保存一段代碼,在需要的時候調(diào)用,目前Block已經(jīng)廣泛應(yīng)用于iOS開發(fā)中。Block實(shí)際上是OC語言對閉包的實(shí)現(xiàn),是帶有自動變量值的匿名函數(shù)。Block既可以定義在函數(shù)內(nèi)部也可以定義在函數(shù)外部。只有在調(diào)用block的時候,block{}內(nèi)的代碼才會執(zhí)行。

1.2 閉包的概念

閉包就是一個函數(shù),或者一個指向函數(shù)的指針,加上這個函數(shù)執(zhí)行的非局部變量。閉包允許一個函數(shù)訪問聲明該函數(shù)運(yùn)行上下文中的變量,甚至可以訪問不同運(yùn)行上文中的變量。

1.3 block的聲明與定義

 int (^myBlock)(int) = ^(int num){

return num*num;

};

int 代表返回類型 ^代表的這是一個block

myBlock代表block的名字 (int)代表參數(shù)類型

(int num)代表形參的類型及名字

在定義block的時候一定不要忘記在最后加上分號

block是一個對象,內(nèi)部是一個結(jié)構(gòu)體,在創(chuàng)建block 的時候就把它的指針傳給了block,所以可以直接調(diào)用block塊

但是為了使用block方便,一般在定義block的時候會用typedef簡化block如:typedef void(^myBlock)(int) ;

1.4 block的實(shí)質(zhì)

通俗點(diǎn)說,block是一個結(jié)構(gòu)體,它里面包含了函數(shù)指針以及block外部上下文變量等信息,這個函數(shù)指針指向的是在定義block時的代碼塊。

1.5 block最常使用的情況

block最常使用的方式:界面?zhèn)髦?;回調(diào)

2 Block對變量的訪問與修改

2.1 block對局部變量的訪問與修改

(1) block1 = ^{
        NSLog(@"哈哈哈哈哈哈");
    };
    block1();

(2) NSInteger aaa = 100;
    block2 = ^{
        aaa++;    //會報錯
        NSLog(@"aaa的值為%ld",aaa);
    };

(3)__block NSInteger aaa = 100;
    block2 = ^{
        aaa++;  
        NSLog(@"aaa的值為%ld",aaa);   //aaa = 101
    };

(4) NSInteger aaa = 100;
    block5 = ^{
        NSLog(@"aaa的值為%ld",aaa);//aaa = 100
    };
    aaa = 105;
    block5();

(5)__block NSInteger ddd = 10;
    block7 = ^{
        ddd = ddd +1;
        NSLog(@"ddd現(xiàn)在的值為%ld",ddd);
    };
    ddd = 100;
    block7();

通過(1)說明block只有在調(diào)用的時候其內(nèi)部代碼才會執(zhí)行,不調(diào)用其內(nèi)部代碼不會執(zhí)行,通過(2)和(3)說明block可以訪問局部變量,但是不能對其進(jìn)行修改,如果要在block里面修改局部變量的值,那么要在局部變量前面加_ block修飾符 。通過(4)和(5)可以看出如果局部變量不加 block來進(jìn)行修飾,那么當(dāng)在block定義后block調(diào)用前修改局部變量的值,那么block里面的局部變量依然是block定義時的值(因?yàn)樵赽lock定義的時候就將局部變量的值傳給了block所指向的結(jié)構(gòu)體,所以局部變量的改變不會影響block里面的局部變量,但是block里面的局部變量也是不可修改的),如果局部變量前面加 _block修飾,那么當(dāng)在block定以后block執(zhí)行前對局部變量進(jìn)行修改,block里面的局部變量值也會進(jìn)行相應(yīng)的改變(因?yàn)橛胈_block聲明局部變量后,在block里面是把局部變量的指針傳給block指向的結(jié)構(gòu)體的,所以其值可以被改變)。

2.2block對全局變量的訪問

(1)// 聲明全局變量global
int global = 100;
void(^myBlock)() = ^{
   NSLog(@"global = %d", global);
};// 調(diào)用后控制臺輸出"global = 100"
myBlock();
(2)// 聲明全局變量global
int global = 100;
void(^myBlock)() = ^{
 NSLog(@"global = %d", global);// 調(diào)用后控制臺輸出"global = 101"
};
global = 101;
myBlock();
(3)// 聲明全局變量global
int global = 100;
void(^myBlock)() = ^{
    global ++;
   NSLog(@"global = %d", global);// 調(diào)用后控制臺輸出"global = 101"
};
myBlock();

結(jié)論:block可以直接對全局變量進(jìn)行訪問和修改(全局變量所占用的內(nèi)存只有一份,供所有函數(shù)共同調(diào)用,在Block定義時并未將全局變量的值或者指針傳給Block變量所指向的結(jié)構(gòu)體,因此在調(diào)用Block之前對局部變量進(jìn)行修改會影響B(tài)lock內(nèi)部的值,同時內(nèi)部的值也是可以修改的)

2.3 block對全局靜態(tài)變量的訪問

(1)// 聲明靜態(tài)變量global
static int global = 100;
void(^myBlock)() = ^{
   NSLog(@"global = %d", global);// 調(diào)用后控制臺輸出"global = 100"
};
myBlock();
(2)// 聲明靜態(tài)變量global
static int global = 100;
void(^myBlock)() = ^{
    NSLog(@"global= %d", global);// 調(diào)用后控制臺輸出"global = 101"
};
global = 101;
myBlock();
(3)// 聲明靜態(tài)變量global
static int global = 100;
void(^myBlock)() = ^{
    global ++;
   NSLog(@"global = %d", global);// 調(diào)用后控制臺輸出"global = 101"
};
myBlock();

結(jié)論:block里面可以直接訪問和修改靜態(tài)全局變量(因?yàn)樵贐lock定義時便是將靜態(tài)變量的指針傳給Block變量所指向的結(jié)構(gòu)體,因此在調(diào)用Block之前對靜態(tài)變量進(jìn)行修改會影響B(tài)lock內(nèi)部的值,同時內(nèi)部的值也是可以修改的)

3 block的使用方式

block的使用與代理很類似,代理是讓某個類滿足自己的某個協(xié)議,然后實(shí)現(xiàn)協(xié)議里面的方法。Block是自己在本類定義了一個代碼塊,但是在需要的時候才會去調(diào)用。下面是一個block使用的小列子:
業(yè)務(wù)需求是在一個封裝的view中點(diǎn)擊了tableView的某一行,然后通過block把這一行上的字符串傳出去進(jìn)行處理,下面是具體實(shí)現(xiàn):

view的.h文件:
typedefvoid (^ChooseViewClick)(NSString*);
@interfaceChooseView : UIView<UITableViewDataSource,UITableViewDelegate>
@property(nonatomic,copy)ChooseViewClick chooseClick;
@end

當(dāng)tableView的某一行被點(diǎn)擊時:

- (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [self dismiss];
    NSString *str =_showArray[indexPath.row];
    if(self.chooseClick) {
       self.chooseClick(str);
    }
 }

在另一個控制器進(jìn)行處理

 _chooseView.chooseClick = ^(NSString *str)
  {
           _selectTag = 0;
            [btnsetTitle:str forState:UIControlStateNormal];
        };

4 block使用之避免循環(huán)引用

在使用block的時候要特別注意的是避免循環(huán)引用,循環(huán)引用指的是兩個對象都相互強(qiáng)引用了對方,從而導(dǎo)致誰也釋放不了,引起內(nèi)存泄露問題。最常見的分為兩種情況,一種是這個對象擁有這個block,但是在block塊里面又引用了這個對象本身,第二種是block是宿主的一個屬性,但是在block里面又訪問了宿主的其他屬性。
第一種情況,block是self本身的一個屬性,在block里面又訪問了宿主本身:

self.myblock = ^{
[self doSomething];
};

在這里會引起循環(huán)引用,解決辦法是用__weak修飾,正確代碼如下:

__weak typedef(self) weakSelf = self;
self.myblock = ^{
[weakSelf doSomething];
};

第二種情況,block是宿主本身的屬性,在block里面又訪問了宿主的其他屬性:

self.myblock = ^{
[self.muArray removeAllObjects];
};    //引起循環(huán)引用

此時解決方案跟上面一樣,可以加__block 或者_(dá)_weak 來修飾,正確代碼如下:

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

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

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