最全解析Blcok

一、何為Block?

普遍的解釋是:帶有自動(dòng)變量(局部變量)的匿名函數(shù)。
其本質(zhì)是個(gè)結(jié)構(gòu)體。

  1. 匿名函數(shù) : 不帶有名字的函數(shù)
  2. 自動(dòng)變量 : 局部變量(可以傳遞值的變量),表現(xiàn)為 “截取自動(dòng)變量值”。Block 表達(dá)式截獲所使用的自動(dòng)變量的值,即保存該自動(dòng)變量的瞬間值。修飾為 __block 的變量,在捕獲時(shí),獲取的不再是瞬間值。
 int b = 0;
    void (^myBlock)() = ^{
        NSLog(@"b=%d",b);
    };
    b = 1;
    myBlock();
    //  結(jié)果:b=0

雖然我們在調(diào)用myBlock之前改變了b的值,但是輸出的還是Block編譯時(shí)候b的值,所以截獲瞬間自動(dòng)變量就是:在Block中會(huì)保存變量的值,而不會(huì)隨變量的值的改變而改變。

Block聲明

返回值類型 block名稱 參數(shù)列表
void(^ BlockName)(int a)

Block定義

^ 返回值類型 參數(shù)列表 表達(dá)式
^ void(int count){return count + 1}

注:返回值類型,參數(shù)列表都可以被省略
常用情況是使用typedef定義Block類型,如下:

typedef void (^ TestBlock)(NSString *sendValue);
@property (nonatomic, copy) TestBlock testBlock;

if (self.testBlock) {
      self.testBlock (@"TestValue");
}

__weak typeof(self) weakSelf = self;
testObject.testBlock = ^(NSString *sendValue){
      __strong typeof(weakSelf) strongSelf = weakSelf;
     strongSelf.testValue = sendValue;
};

Block作為參數(shù)使用:

// 1.定義一個(gè)形參為Block的OC函數(shù)
- (void)useBlock:(int(^)(int, int))aBlock
{
    NSLog(@"result = %d", aBlock(300,200));
}
// 2.聲明并賦值定義一個(gè)Block變量
int(^addBlock)(int, int) = ^(int x, int y){
    return x+y;
};
// 3.以Block作為函數(shù)參數(shù),把Block像對象一樣傳遞
[self useBlock:addBlock];

// 將第2點(diǎn)和第3點(diǎn)合并一起,以內(nèi)聯(lián)定義的Block作為函數(shù)參數(shù)
[self useBlock:^(int x, int y){
    return x+y;
}];
在 Objective-C 語言中,一共有 3 種類型的 block:
1.  _NSConcreteGlobalBlock 全局的靜態(tài) block,不會(huì)訪問任何外部變量。
2.  _NSConcreteStackBlock 保存在棧中的 block,當(dāng)函數(shù)返回時(shí)會(huì)被銷毀。
3.  _NSConcreteMallocBlock 保存在堆中的 block,當(dāng)引用計(jì)數(shù)為 0 時(shí)會(huì)被銷毀。
Block屬性的聲明,需要用copy修飾符

Block 的生命周期是和棧是綁定在一起的,隨時(shí)都有可能被釋放,為了不被提前釋放掉,需要 copy 之后讓 block 在堆上?;蛘哒f是為了讓Block在初始化作用域外可以進(jìn)行正常訪問外部變量。

修改 block 之外的變量

對于block外的變量引用,block默認(rèn)是將其復(fù)制到其數(shù)據(jù)結(jié)構(gòu)中來實(shí)現(xiàn)訪問的,如下圖:


通過block進(jìn)行閉包的外部變量是const只讀的。也就是說不能在block中直接修改這些變量,否則,編譯器會(huì)報(bào)錯(cuò)。
** 注:Block中可以直接修改靜態(tài)變量、全局變量**
當(dāng)我們需要在block中處理變量時(shí),可以用__block關(guān)鍵字來聲明變量,這樣就可以在block中修改變量了。
對于用__block修飾的外部變量引用,block是復(fù)制其引用地址來實(shí)現(xiàn)訪問的,如下圖:

block引起的內(nèi)存泄漏

在Block的內(nèi)存存儲(chǔ)在堆中時(shí),如果在Block中引用了外面的對象,會(huì)對所引用的對象進(jìn)行強(qiáng)引用,但是在Block被釋放時(shí)會(huì)自動(dòng)去掉對該對象的強(qiáng)引用,所以不會(huì)造成內(nèi)存泄漏。

Person *p = [[Person alloc] init];

void(^myBlock)() = ^{
    NSLog(@"------%@", p);
};
myBlock();

// Person對象在這里可以正常被釋放

如果對象內(nèi)部有一個(gè)Block屬性,而在Block內(nèi)部又訪問了該對象,那么會(huì)造成循環(huán)引用

@interface Person : NSObject
@property (nonatomic, copy) void(^myBlock)();
@end

@implementation Person
- (void)dealloc
{
    NSLog(@"Person dealloc");
}
@end
Person *p = [[Person alloc] init];
//  情況一
p.myBlock = ^{
    NSLog(@"------%@", p);
};
//  情況二
- (void)resetBlock
{
    self.myBlock = ^{
        NSLog(@"------%@", self);
    };
}
p.myBlock();

// 情況一:因?yàn)閙yBlock作為Person的屬性,采用copy修飾符修飾(這樣才能保證Block在堆里面,以免Block在棧中被系統(tǒng)釋放),所以Block會(huì)對Person對象進(jìn)行一次強(qiáng)引用,導(dǎo)致循環(huán)引用無法釋放
// 情況二:Person對象在這里無法正常釋放,在resetBlock方法實(shí)現(xiàn)中,Block內(nèi)部對self進(jìn)行了一次強(qiáng)引用,導(dǎo)致循環(huán)引用無法釋放

解決方案:解決循環(huán)引用的辦法是使用一個(gè)弱引用的指針指向該對象即用__weak修飾,然后在Block內(nèi)部使用該弱引用指針來進(jìn)行操作,這樣避免了Block對對象進(jìn)行強(qiáng)引用

//  情況一
__weak typeof(p) weakP = p;
//  情況二
__weak typeof(self) weakSelf = self;

But有可能會(huì)出現(xiàn)這種情況:Block是通過弱引用指向了一個(gè)對象,那么有可能在調(diào)用Block之前這個(gè)對象便已經(jīng)被釋放了,所以我們需要在Block內(nèi)部再定義一個(gè)強(qiáng)指針來指向該對象。

__weak typeof(self) weakSelf = self;
testObject.testBlock = ^(NSString *sendValue){
      __strong typeof(weakSelf) strongSelf = weakSelf;
     strongSelf.testValue = sendValue;
};

在Block內(nèi)部定義的變量,會(huì)在作用域結(jié)束時(shí)自動(dòng)釋放,Block對其并沒有強(qiáng)引用關(guān)系,且在ARC中只需要避免循環(huán)引用即可,如果只是Block單方面地對外部變量進(jìn)行強(qiáng)引用,并不會(huì)造成內(nèi)存泄漏

親,喜歡的話點(diǎn)個(gè)贊唄!

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

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

  • Block使用場景,可以在兩個(gè)界面的傳值,也可以對代碼封裝作為參數(shù)的傳遞等。用過GCD就知道Block的精妙之處。...
    Coder_JMicheal閱讀 821評(píng)論 2 1
  • iOS代碼塊Block 概述 代碼塊Block是蘋果在iOS4開始引入的對C語言的擴(kuò)展,用來實(shí)現(xiàn)匿名函數(shù)的特性,B...
    smile刺客閱讀 2,468評(píng)論 2 26
  • 前言 Blocks是C語言的擴(kuò)充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了這...
    小人不才閱讀 3,869評(píng)論 0 23
  • 近來把《iOS與OS X多線程和內(nèi)存管理》這本書又掏出來看了一遍,這本書前前后后加起來看了能有三四遍了,每次看都有...
    老司機(jī)Wicky閱讀 2,422評(píng)論 5 46
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,687評(píng)論 18 399

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