本文已經新增到**:《徹底弄懂oc》。 歡迎加入我的qq群:661461410
,一起**ios底層原理。
block的本質是什麼?你能講出來它的底層結構嗎?
全域性變數會被block捕獲嗎?block會捕獲哪些變數?
block又叫**塊,是oc語法中非常重要的乙個概念,我們先來看一下block的簡單使用。
int main(int argc, const char * ar**) ();
int d = 5;
void (^block)(int, int) = ^(int a, int b) ;
block(3, 4);
}return 0;
}
上面的**中,我們建立了兩個block,乙個直接執行,輸出hello world
。 乙個通過block變數進行呼叫,並引用了乙個外部變數d。輸出12
。
我們將以上**編譯成c**:
# 在main.m所在目錄執行該命令。
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
從main-arm64.cpp檔案中,我們可以看到block的結構如下:
struct __main_block_impl_1
};struct __block_impl ;
我們可以看出block的底層是結構體,__main_block_impl_1
包含乙個變數impl
其結構和class
的結構類似,其包含乙個isa
指標,可見block本質上也是乙個類,其中funcptr
表示要執行的**塊的函式位址。d
表示它引用的外部變數。
下面,我們一起看一下block的呼叫過程,首先我們將下面**,編譯成c**。
int main(int argc, const char * ar**) ;
block();
}return 0;
}// 下面是編譯後的c**
int main(int argc, const char * ar**)
return 0;
}static void __main_block_func_0(struct __main_block_impl_0 *__cself)
static struct __main_block_desc_0 __main_block_desc_0_data = ;
針對,上面的兩行**,先呼叫__main_block_impl_0
的結構體建構函式,建立block,並將位址賦值給我block。而__main_block_func_0
是對應block內部要執行的**,是乙個靜態的方法,它會賦值給__block_impl
中的funcptr
。
__main_block_desc_0_data
是也是乙個結構體變數,裡面的兩個引數reserved
為 0,block_size
為__main_block_impl_0
結構體的大小。
呼叫的時候,是從block裡面直接取出funcptr
。 我們知道block
是__main_block_impl_0
型別,由於結構體的特性,將block
強轉為__block_impl
型別,是可以直接取到funcptr
的。所以第二句的呼叫也是清晰的。
上面的兩句**去掉強制型別轉化,可以精簡為:
void
(*block)
(void)=
&__main_block_impl_0
(__main_block_func_0,
&__main_block_desc_0_data));
block->
funcptr
(block)
;
這樣,呼叫過程就清晰多了。
通過上面的分析,我們可以看出block的結構應該是如下圖所示:
auto自動變數是離開作用域,就會銷毀, 只存在區域性變數裡面,不能修飾全域性變數。
比如,下面例子中的age
和weight
就是auto變數,他們離開自己所在的作用局就會銷毀。預設情況下auto關鍵字會自動新增。
int main(int argc, const char * ar**)
// 在這裡訪問age, weight就報錯了。
}return 0;
}
如果block中使用了auto變數,那麼block就會捕獲該變數,下面**
int main(int argc, const char * ar**) ;
age = 40;
block();}}
return 0;
}
列印的結果中 age為20 還是 40? 編譯後,__main_block_impl_0
的結構如下,增加了兩個int 變數。
struct __main_block_impl_0
};
我們可以看出,捕獲了auto變數,而且是值傳遞。
static變數
下面**輸入結果是什麼?
int main(int argc, const char * ar**) ;
height = 80;
block();}}
return 0;
}
結果是80,為什麼呢? 我們依然通過編譯後的結果檢視。
struct __main_block_impl_0 };
intmain
(int argc,
const
char
* ar**)
}return0;
}static
void
__main_block_func_0
(struct __main_block_impl_0 *__cself)
我們可以看出__main_block_impl_0
中增加了乙個變數height
,但需要注意的是它是int *
型別的,在給它賦值的時候傳入的是&height
。 在__main_block_func_0
中訪問的時候是通過*height
取值的。
因此我們可以得出結論,靜態變數也是會被block捕獲的,但它捕獲的是指標。
全域性變數
下面**,輸出的結果是什麼?
int age = 10;
int main(int argc, const char * ar**) ;
age = 20;
block();}}
return 0;
}
輸入結果是20,那block捕獲了age嗎?是通過指標訪問的嗎?我們看一下編譯結果:
struct __main_block_impl_0
};
可以看出block並沒有捕獲全域性變數。
結論通過上面的分析,我們可以得出結論:
下面**中,person類中的test方法中block捕獲了變數了嗎?捕獲了那個變數?
// main.m
#import #import "person.h"
int main(int argc, const char * ar**)
return 0;
}// person.h
@inte***ce person : nsobject
@property (nonatomic, copy)nsstring *name;
- (void)test;
@end
// person.m
@implementation person
- (void)test ;
block();
}@end
用上面的命令將person.m編譯c++**,如下:
struct __person__test_block_impl_0
};
可以看出,這裡捕獲的並不是name,而是person物件,這涉及了block的迴圈引用,我們將在下面的文章中講述。
下面各個**的輸出結果是什麼?
//問題1
int main(int argc, const char * ar**) ;
array[0] = @"dgf";
block();
}return 0;
}//問題2
int main(int argc, const char * ar**) ;
array = [@[@"dgf"] mutablecopy];
block();
}return 0;
}
Block底層實現
步驟一 建立乙個控制台應用,在main中新增塊的測試 import int main int argc,const char ar testblock return 0 步驟二 開啟終端,定位到main.m檔案目錄,錄入xcode命令 xcrun sdk iphoneos clang arch ar...
block的底層實現原理
block就是指向結構體的指標,編譯器會將block的內部 生成對應的函式,利用這個指標就可以呼叫這個函式.普通的區域性變數是值傳遞,用 block static 或者是全域性變數就是位址傳遞 block的記憶體預設是存放在棧裡面的,他不會對所引用的物件進行操作 如果對block做一次copy操作b...
OC底層 block內修改變數
講一下以上兩個問題 對於捕獲問題,只有區域性變數才能 獲,全域性變數不需要捕獲,直接拿著使用,但是有一點,當block在棧中時,block是不能捕獲物件型變數,直接拿著使用,只有在堆中的block才能捕獲物件型變數 對於block內部修改問題,因為變數捕獲有兩種,一種是值捕獲,一種是位址捕獲,對於這...