oc-block相關
阿新 • • 發佈:2019-12-25
一、三種block
1、全域性block
- (void)viewDidLoad
{
[super viewDidLoad];
void (^block)(void) = ^{
NSLog(@"hehe");
};
NSLog(@"%@",block); // 列印block物件
}
--------------------
列印:
<__NSGlobalBlock__:0x105f14088> //說明這是一個全域性block
複製程式碼
2、堆block
- (void)viewDidLoad
{
[super viewDidLoad];
int a = 10;
void (^block)(void) = ^{
NSLog(@"hehe - %d",a);
};
NSLog(@"%@",block); // 列印block物件
}
--------------------
列印:
<__NSMallocBlock__:0x600002cceeb0> //說明這是一個堆block
複製程式碼
3、棧block
- (void)viewDidLoad
{
[super viewDidLoad];
int a = 10;
NSLog(@"%@",^{
NSLog(@"hehe - %d",a);
}); // 列印block物件
}
--------------------
列印:
<__NSStackBlock__:0x7ffee09cf800> //說明這是一個堆block
原因:程式碼塊在做=的賦值操作的時候隱藏了一個copy,從棧區拷貝到的堆區
複製程式碼
棧:0x7
堆:0x6
靜態變數:0x1
二、block的迴圈引用
1、引用計數與物件的釋放
- 當物件A引用物件B時,B的rentainCount +1
- 當A進行dealloc時,向B傳送release訊號,B的retainCount -1
- 當B的retainCount == 0時,B就會呼叫dealloc
2、迴圈引用
A引用了B,此時B的rentainCount==1,然後B引用了A,此時A的rentainCount==(n+1),所以A和B都無法呼叫dealloc
typedef void(^HHBlock)(void);
@interface ViewController ()
@property (nonatomic,copy) HHBlock block;
@property (nonatomic,copy) NSString *name;
@end
@implementation ViewController
// 迴圈引用版
- (void)viewDidLoad
{
[super viewDidLoad];
self.name = @"hehe";
self.block = ^{
self.name = @"haha";
};
/*
此時,self 持有block,同時,block持有self
*/
self.block;
}
// 升級版
- (void)viewDidLoad
{
[super viewDidLoad];
self.name = @"hehe";
__weak typeof(self) weakSelf = self;
// 此處開始self引用計數不會因為block持有而增加
self.block = ^{
__strong typeof(self) strongSelf = weakSelf;
// 此處臨時變數會在autoreleasepool中自動釋放
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2 * NSEC_PER_SEC)),dispatch_get_main_queue(),^{
strongSelf.name = @"haha";
});
};
self.block;;
}
// 升級版 2
- (void)viewDidLoad
{
[super viewDidLoad];
self.name = @"hehe";
__block ViewController *vc = self;
// __block可以copy一份當前的物件到新建的struct裡面
// 目前self持有block持有vc持有self
self.block = ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,^{
vc.name = @"haha";
vc = nil; //直接給變數釋放,不置為nil還是迴圈引用不能自動釋放
//目前self持有block持有nil不再持有self
});
};
self.block;;
}
- (void)dealloc
{
NSLog(@"呼叫了dealloc");
}
複製程式碼
// 騷騷版 3
typedef void(^HHBlock)(ViewController *);
@interface ViewController ()
@property (nonatomic,copy) NSString *name;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.name = @"hehe";
self.block = ^(ViewController *vc){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,^{
vc.name = @"haha";
});
};
self.block(self);
}
- (void)dealloc
{
NSLog(@"呼叫了dealloc");
}
複製程式碼
三、bolck自動捕獲變數的特性
1、引出問題
- 物件的底層:struct
- block是個物件
- block捕獲變數 -->即--> struct內部增加屬性
NSLog的本質是什麼?
答:是一套封裝了print的C語言函式,比較耗時
- (void)viewDidLoad
{
__block int a = 10;
NSLog(@"進去之前:%p",&a); // 0x7ffee3deb988 - 棧
viod(^block)(void) =^{
// 捕獲
a++;
NSLog(@"在裡面:%p",&a); // 0x600002647c78 - 捕獲後 到 堆區
};
NSLog(@"寫完block:%p",&a); // 0x600002647c78 - 看寫程式碼的順序,不要看呼叫順序
block();
}
複製程式碼
2、原始碼初探
// C語言
#include "stdio.h"
int main()
{
void(^block)(void) = ^{
printf("hehe");
};
block;
return 0;
}
---------------------- 執行clang看其內部實現
clang -rewrite-objc testBlcok.c -o blockCPP.cpp
---------------------- 結果很亂但是不用看,看下面簡化版的就行
int main()
{
void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0,&__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 0;
}
---------------------- 簡化版
int main()
{
void(*block)(void) = __main_block_impl_0(__main_block_func_0,__main_block_desc_0_DATA); // f(a,b)
block->FuncPtr(block); // 其實就是 block();
return 0;
}
複製程式碼
// __main_block_impl_0
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp,struct __main_block_desc_0 *desc,int flags=0)
{
// 初始化
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
// 儲存程式碼塊的能力
impl.FuncPtr = fp; // 屬性函式 - 儲存函式 - __main_block_func_0 其實就是^{ ··· }程式碼塊
Desc = desc;
}
};
複製程式碼
3、捕獲變數的程式碼
-
不加__block的int
#include "stdio.h" int main() { int a = 10; void(^blockNN)(void) = ^{ printf("--- %d",a); }; blockNN(); return 0; } ------------ int main() { int a = 10; void(*blockNN)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0,&__main_block_desc_0_DATA,a)); ((void (*)(__block_impl *))((__block_impl *)blockNN)->FuncPtr)((__block_impl *)blockNN); return 0; } ------------ int main() { int a = 10; void(*blockNN)(void) = __main_block_impl_0(__main_block_func_0,__main_block_desc_0_DATA,a)); // f(a,b,c) (blockNN->FuncPtr)(blockNN); return 0; } 複製程式碼
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int a; // 自動捕獲到了變數 __main_block_impl_0(void *fp,int _a,int flags=0) : a(_a) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; 複製程式碼
static void __main_block_func_0(struct __main_block_impl_0 *__cself) { // 建立新的變數 // __cself就是(blockNN->FuncPtr)(blockNN)中的blockNN int a = __cself->a; // bound by copy printf("--- %d",a); } 複製程式碼
-
加__block的int
#include "stdio.h" int main() { __block int a = 10; void(^blockNN)(void) = ^{ printf("--- %d",a++); }; blockNN(); return 0; } ---------------- int main() { __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a,0,sizeof(__Block_byref_a_0),10}; void(*blockNN)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0,570425344)); ((void (*)(__block_impl *))((__block_impl *)blockNN)->FuncPtr)((__block_impl *)blockNN); return 0; } ----------------- int main() { // 生成了一個含有五個屬性的結構體 __Block_byref_a_0 a = { 0,&a,// 引用原來的a的地址 0,10 // 原來的a的值 }; void(*blockNN)(void) = __main_block_impl_0(__main_block_func_0,570425344)); // 其中的&a就是上面結構體的指標地址 ((void (*)(__block_impl *))((__block_impl *)blockNN)->FuncPtr)((__block_impl *)blockNN); return 0; } 複製程式碼
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __Block_byref_a_0 *a; // by ref // 自動捕獲__Block_byref_a_0型別的*a的指標 __main_block_impl_0(void *fp,__Block_byref_a_0 *_a,int flags=0) : a(_a->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; 複製程式碼
static void __main_block_func_0(struct __main_block_impl_0 *__cself) { // 指標傳遞 __Block_byref_a_0 *a = __cself->a; // bound by ref // 此*a就是原來的結構體__Block_byref_a_0 a,包含了原來a的地址和值 printf("--- %d",(a->__forwarding->a)++); } 複製程式碼
-
總結
沒有
__block
修飾的int a
只是傳進來一個值,沒有原來的a的地址,不能修改,是隻讀狀態有
__block
修飾的int a
就是指標傳遞,可讀可寫