咱們知道應用程序的內存分配有四個區:程序員
那麼block做爲OC對象是建立在哪一個區上呢?這篇文章咱們來探究一下。bash
block是一個NSBlock
對象,NSBLock
的聲明以下:數據結構
@interface NSBlock : NSObject <NSCopying>
+ (id)alloc;
+ (id)allocWithZone:(struct _NSZone { }*)arg1;
- (id)copy;
- (id)copyWithZone:(struct _NSZone { }*)arg1;
- (void)invoke;
- (void)performAfterDelay:(double)arg1;
@end
複製代碼
NSBlock
有三個子類,分別是:框架
接下來咱們分別研究一下這三種block。函數
_NSConcreteGlobalBlock
,顧名思義,全局block。如下兩種狀況初始化block時,生成的block爲_NSConcreteGlobalBlock
:ui
代碼以下:atom
/** 全局變量 */
int global_count = 10;
/** 靜態全局變量 */
static int static_global_count = 10;
int main(int argc, const char * argv[]) {
/** 靜態局部變量 */
static int static_count = 10;
void (^block)(void) = ^ {
global_count = 11;
static_global_count = 11;
static_count = 11;
};
block();
return 0;
}
複製代碼
咱們打斷點看一下,捕獲捕獲全局變量或者靜態局部變量時,block爲_NSConcreteGlobalBlock
,斷點截圖以下:spa
_NSConcreteStackBlock
爲棧block,在ARC環境下,捕獲局部變量、成員變量,且沒有被強引用的block都是_NSConcreteStackBlock
。操作系統
OC代碼以下:code
int main(int argc, const char * argv[]) {
/** 局部變量 */
int count = 10;
/** 執行 或者使用void (^__weak block)(void)來指向block */
^{
NSLog(@"%d", count);
}();
// 打印Block對象
NSLog(@"Block對象:%@", ^{
NSLog(@"%d", count);
});
return 0;
}
複製代碼
控制檯打印的信息以下:
Block對象:<__NSStackBlock__: 0x7ffeefbff568>
複製代碼
OC代碼以下:
@interface ViewController ()
@property (nonatomic, assign) int count;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(@"Block對象:%@", ^{
NSLog(@"%d", self.count);
});
^{
NSLog(@"%d", self.count);
}();
}
@end
複製代碼
控制檯打印的信息以下:
Block對象:<__NSStackBlock__: 0x7ffee554b838>
複製代碼
總結來講,block實現的內容依賴於執行時的狀態,而且沒有被強引用,且做用域僅爲當前函數時,block爲_NSConcreteStackBlock
,保存在棧上。
_NSConcreteMallocBlock
爲堆block,爲廣域變量。在如下狀況會被保存在堆上:
當block做爲函數的返回值時,ARC環境下自動將block拷貝,擴大做用域,OC代碼以下:
/** 函數建立 */
typedef void (^block)(void);
block function(int num) {
return ^{
NSLog(@"%d", num);
};
}
int main(int argc, const char * argv[]) {
// 函數的返回值賦值
void (^__weak block1)(void) = function(10);
// 打印block1對象
NSLog(@"block1對象:%@", block1);
return 0;
}
複製代碼
控制檯打印的結果以下:
block1對象:<__NSMallocBlock__: 0x103804d30>
複製代碼
總結一下:由於block在函數內做爲局部變量,若是不進行拷貝的話,函數返回時block就銷燬了,因此ARC下系統幫咱們自動拷貝,MRC下直接編譯報錯,須要咱們手動拷貝block。
系統對於block做爲參數時自動進行拷貝暫時沒有時間證明,以後我會補上這部份內容。抱歉。。。
很少說,直接上表格:
Block的類 | 副本源的配置存儲域 | 賦值效果 |
---|---|---|
_NSConcreteGlobalBlock | 數據區 | 什麼也不作 |
_NSConcreteStackBlock | 棧 | 從棧拷貝到堆 |
_NSConcreteMallocBlock | 堆 | 引用計數增長 |
數據區中的_NSConcreteGlobalBlock
執行拷貝操做什麼也不作的緣由是不須要,由於_NSConcreteGlobalBlock
在進程結束後才銷燬,已是廣域變量了。
判斷block是何種類型的變量分如下幾種狀況:
_NSConcreteGlobalBlock
_NSConcreteStackBlock
_NSConcreteStackBlock
而且被強引用則爲_NSConcreteMallocBlock