block的使用淺析

1 block的基本概念

1.1 block的產生和用途

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

1.2 閉包的概念

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

1.3 block的聲明與定義

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

return num*num;

};

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

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

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

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

block是一個對象,內部是一個結構體,在創建block 的時候就把它的指針傳給了block,所以可以直接調用block塊

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

1.4 block的實質

通俗點說,block是一個結構體,它里面包含了函數指針以及block外部上下文變量等信息,這個函數指針指向的是在定義block時的代碼塊。

1.5 block最常使用的情況

block最常使用的方式:界面傳值;回調

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現在的值為%ld",ddd);
    };
    ddd = 100;
    block7();

通過(1)說明block只有在調用的時候其內部代碼才會執行,不調用其內部代碼不會執行,通過(2)和(3)說明block可以訪問局部變量,但是不能對其進行修改,如果要在block里面修改局部變量的值,那么要在局部變量前面加_ block修飾符 。通過(4)和(5)可以看出如果局部變量不加 block來進行修飾,那么當在block定義后block調用前修改局部變量的值,那么block里面的局部變量依然是block定義時的值(因為在block定義的時候就將局部變量的值傳給了block所指向的結構體,所以局部變量的改變不會影響block里面的局部變量,但是block里面的局部變量也是不可修改的),如果局部變量前面加 _block修飾,那么當在block定以后block執行前對局部變量進行修改,block里面的局部變量值也會進行相應的改變(因為用__block聲明局部變量后,在block里面是把局部變量的指針傳給block指向的結構體的,所以其值可以被改變)。

2.2block對全局變量的訪問

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

結論:block可以直接對全局變量進行訪問和修改(全局變量所占用的內存只有一份,供所有函數共同調用,在Block定義時并未將全局變量的值或者指針傳給Block變量所指向的結構體,因此在調用Block之前對局部變量進行修改會影響Block內部的值,同時內部的值也是可以修改的)

2.3 block對全局靜態變量的訪問

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

結論:block里面可以直接訪問和修改靜態全局變量(因為在Block定義時便是將靜態變量的指針傳給Block變量所指向的結構體,因此在調用Block之前對靜態變量進行修改會影響Block內部的值,同時內部的值也是可以修改的)

3 block的使用方式

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

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

當tableView的某一行被點擊時:

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

在另一個控制器進行處理

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

4 block使用之避免循環引用

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

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

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

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

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

self.myblock = ^{
[self.muArray removeAllObjects];
};    //引起循環引用

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

__block typedef(self) weakSelf = self;
self.myblock = ^{
[weakSelf.muArray removeAllObject];
};
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容