block計算機組成原理?
block的本質是一個結構體,包含引用的外部變量及一個需要執行的函數的函數指針,在內存中可以有三個位置,即堆上、棧上和全局區(靜態區)。當block中沒有引用外部變量時,block的位置在全局區,當block中訪問外部變量時,MRC下block默認在棧區,ARC下block默認會從棧區復制到堆區。
另外,block在生成對應的結構體時,如果引用的外部變量是strong類型的,那么在結構體內部生成的對應成員變量就是strong類型的;如果引用的外部變量是weak類型的,那么在結構體內部生成的對應的成員變量就是weak類型的;如果是基本類型的,那么生成的對應的成員變量就是基本類型的。block的函數在執行時,會在函數體內部創建局部變量,賦值成對應的block結構體內的成員變量,然后再用這些局部變量去做對應的操作,這也是普通情況下(外部變量不加__block關鍵字)block內部不能改變外部變量的原因。所以block在創建的時候實際上就已經對其中引用的外部變量有內存操作了(strong類型的引用計數會加1),在block的函數體執行過程當中,外部變量的引用計數也會有相應的增減。
__block關鍵字原理
通常情況下,block的函數體中是不能更改引用的外部變量的值的(但是可以改這個變量的某個屬性值,如果這個變量是個OC類型的變量,因為對象還是同一個對象),但是如果外部變量用了__block關鍵字修飾,那么block函數體內部就可以更改這個外部變量的值。原理是在加了__block關鍵字之后,這個變量會變成一個結構體中的一個成員變量,之后我們再訪問這個外部變量的時候實際上是在訪問這個結構體里對應的成員變量。結構體中會有一個forwarding指針,訪問成員變量時都會通過這個forwarding指針,當這個變量在棧上時forwarding指針指向結構體自身,當這個變量被復制到堆上時棧上的結構體的forwarding指針指向堆上的這個結構體,堆上的結構體的forwarding指針還是指向自身,forwarding指針的作用就是在操作外部變量時,無論是通過棧上的結構體還是堆上對應的結構體,都能夠操作同一個外部變量(即堆上的結構體中的成員變量)。
舉個例子:
-(void)viewDidLoad {
__block int a = 1; //棧上的變量結構體(簡稱a結構體)
a++; // a結構體->forwarding->a++;這時候forwarding指向的是自己
self.myBlock = ^{//a結構體被賦值給block結構體中的成員變量,假設為a1結構體
a++; //block結構體->a1結構體->fowrding->a++;
};
a++; //a結構體->fowrding->a++; 這時候forwarding指向的是堆上的結構體
}
-(IBAction)clickButton {
self.myBlock(); //block結構體->a1結構體->fowrding->a++;
}
原理很簡單:就是block結構體會創建內部成員變量保存外部變量,當外部變量被__block關鍵字修飾時會生成一個對應的變量結構體,block結構體對應的內部成員變量會變成這個變量結構體。(相當于是block結構體保存了__block修飾的外部變量的一個間接引用)