天天看點

block相關知識點

block存儲區域

首先我們得明白iOS系統存儲有哪幾大記憶體區域:

1、棧區

    由編譯器自動配置設定并釋放,存放函數的參數值(實參),局部變量等。棧是向低位址擴充的資料結構,是不連續的記憶體區域,采用後進先出(LIFO )。優點是快速高效,缺點時有限制,資料不靈活。

2、堆區

    由程式員配置設定和釋放,如果程式員不釋放,程式結束時,可能會由作業系統回收。堆是向高位址擴充的資料結構,是不連續的記憶體區域,以連結清單的方式進行存儲。

3、全局靜态區

    全局區分為初始化全局區(bbs)和未初始化全局區(data)。全局變量和靜态變量的存儲是放在一塊的,初始化的全局變量和靜态變量在一塊區域, 未初始化的全局變量和未初始化的靜态變量在相鄰的另一塊區域,在程式結束後有系統釋放。

4、常量區

    存儲常量資料,通常程式結束後由系統自動釋放。

5、代碼區

    用來存放函數的二進制代碼,在運作時要防止被非法修改,隻允許讀取不允許操作。

下面看一段示例

block相關知識點
block相關知識點

block是一個匿名函數,具體自動捕獲外部變量的能力

從上面的示例我們可以發現:

一個普通的不包含任何外部變量的block(比如示例中的1、4、7)是存儲在全局靜态區的,不管這個block作為臨時變量還是屬性或者其他方式;

單純作為block存在,但是有用到外部變量的(比如示例中的1、2、3)存儲在棧區,因為這樣可以更快的通路外部變量;

如果block用=複制給了一個變量,同時block擷取了外部變量(比如示例中的5、6、8、9)存儲在堆區,因為相當于從棧區拷貝到堆區。ARC情況下,可以用copy、strong修飾block,隻是為了習慣,基本都是用的copy。

block循環引用産生情況及解決方法

1、最常見的情況,循環鍊self -> block -> self

block相關知識點

解決方法:

(1)用__weak修飾self

block相關知識點

(2)用一個中間臨時變量儲存self

block相關知識點

(3)将self作為參數傳進block

block相關知識點

2、單純用__weak修飾self有時也會産生一個新的問題

block相關知識點
block相關知識點

因為在 block裡面用到self的屬性時,self已經被釋放,self = nil,是以此時拿到的屬性值為空。

解決方法:__strong再次強引用一遍

block相關知識點

下面再看看 block到底做了什麼,或者說block是怎麼實作的?

現在main.m有這麼一段代碼

block相關知識點

在終端執行clang -rewrite-objc main.m得到main.cpp

block相關知識點

void (^block)(void) = ^{

            NSLog(@"這是一個block");

        };

 block();

反編譯之後,再簡化

block = __main_block_impl_0(__main_block_func_0, __main_block_desc_0_DATA);

block->FuncPtr(block);

block是__main_block_impl_0

block->FuncPtr = fp =  __main_block_func_0 =  ^{  NSLog(@"這是一個block");  };

最後看__block修飾是如何修改外部變量

main.m有這麼一段代碼

block相關知識點

在終端執行clang -rewrite-objc main.m得到main.cpp

block相關知識點

 __block int a = 1;

 void (^block)(void) = ^{

        a++;

   };

  block();

反編譯再簡化

a = {0, &a, 0, sizeof(__Block_byref_a_0), 1};

block = __main_block_impl_0(__main_block_func_0, __main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, flag);

block->FuncPtr(block)

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

  __Block_byref_a_0 *a = __cself->a; // bound by ref     // 現在用的是a的位址,是以block裡面修改a的值,外面會有變化

            (a->__forwarding->a)++;

        }