- Block 結構體寫法
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int age;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself, int a, int b) {
int age = __cself->age; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_3f4c4a_mi_0, age);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_3f4c4a_mi_1);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_3f4c4a_mi_2);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_3f4c4a_mi_3);
}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int age = 10;
// - (void(*)(int,int)) 是強制類型轉換
/* 本質
block = &__main_block_impl_0( __main_block_func_0,
&__main_block_desc_0_DATA,
age));
*/
void (*block)(int, int) = ((void(*)(int,int))&__main_block_impl_0(
(void*)__main_block_func_0,
&__main_block_desc_0_DATA,
age));
// - (void (*)(__block_impl *, int, int)) 是強制類型轉換
/* 本質
block->FuncPtr(block, 10, 10);
*/
((void (*)(__block_impl *, int, int))((__block_impl *)block)->FuncPtr)(
(__block_impl *)block,
10,
10);
}
return 0;
}
- block 中的參數類型
// - 有參數的 block 的數據類型的源碼(以上的 block 的內部結構是clang編譯的結果 具有一定的參考意義 下邊的是逆向工程中的block 的使用)
#import <Foundation/Foundation.h>
// - block 在內存中的結構
typedef struct {
unsigned long int reserved;
unsigned long int size;
void (*copy_helper)(void *dst, void *src);
void (*dispose_helper)(void *src);
const char *signature;
}QGBlockDescriptor;
// - block 在內存中的結構
typedef struct {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, ...);
QGBlockDescriptor *descriptor;
} QGBlockLiteral;
@interface QGBlockTool : NSObject
/** 輸出逆向的 block 的說明 */
+ (NSMethodSignature *)printBlockDescriptor:(QGBlockLiteral *)blockStruct;
@end
#import "QGBlockTool.h"
@implementation QGBlockTool
// - 獲取 block 的返回值和參數的簽名和 block 的實現函數指針
/** 輸出逆向的 block 的說明 */
+ (NSMethodSignature *)printBlockDescriptor:(QGBlockLiteral *)blockStruct{
/** 判斷 block 是否有簽名 */
BOOL hasSign = ((blockStruct -> flags & (1 << 30)) != 0);
if (!hasSign) return nil;
// - block 的函數地址
NSLog(@"調用地址 : %p", blockStruct->invoke);
// - block 的函數指針
QGBlockDescriptor *descriptor = blockStruct->descriptor;
NSLog(@"descriptor : %@", [NSString stringWithUTF8String:descriptor -> signature]);
NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:descriptor -> signature];
return methodSignature;
}
@end
- block 對變量的捕獲 (考慮作用域的問題,需要跨函數訪問,爲了保證block內部能夠正常訪問外部的變量,block有個變量捕獲機制){
- auto自動變量可能會銷燬的,內存可能會消失,不採用指針訪問;static變量一直保存在內存中,指針訪問即可
- block不需要對全局變量捕獲,都是直接採用取值的
}
[示例如下]:
block 對局部變量的捕獲 (a 爲 局部變量, b 爲靜態局部變量)
block 對成員變量和 self 的捕獲(以爲 self 和 成員變量屬於 方法的參數和需要方法的參數查找的變量, 所以也是屬於捕獲局部變量, 需要捕獲)
block 對全局變量的捕獲 (a 爲 全局變量, b 爲靜態全局變量)
[結論]:
- block 對變量的引用操作(爲什麼有__weak typeof(self) weakSelf = self; 和 __strong typeof(self) strongSelf = weakSelf;)
因爲 block 是一個代碼塊, 是由程序員自己調用的, 而程序員調用 block 時候, 可能 block 內部訪問的局部對象已經釋放, 爲了防止這樣已經被釋放的變量再次被訪問而出錯, 所以 block 會對內部的訪問的變量有一次強引用操作, 如下:
代碼:
// - OC 代碼
__block __weak int age = 10;
// - 修改對象類型 也必須要加 __block
__block NSObject *obj = [[NSObject alloc]init];
MJBlock block1 = ^{
__strong int myage = age;
age = 20;
obj = nil;
NSLog(@"age is %d", age);
};
MJBlock block2 = ^{
age = 30;
NSLog(@"age is %d", age);
};
block1();
block2();
// - 訪問成員變量的 block 的內部實現代碼
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
NSObject *p;
__Block_byref_age_0 *age; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *_p, __Block_byref_age_0 *_age, int flags=0) : p(_p), age(_age->__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_age_0 *age = __cself->age; // bound by ref
NSObject *p = __cself->p; // bound by copy
(age->__forwarding->age) = 20;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_e2457b_mi_0, p);
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
_Block_object_assign((void*)&dst->p, (void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
_Block_object_dispose((void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
block 對局部變量的捕捉和引用問題
- 如果block在棧空間,不管外部變量是強引用還是弱引用,block都會弱引用訪問對象
- 如果block在堆空間,如果外部強引用,block內部也是強引用;如果外部弱引用,block內部也是弱引用
[結論 : ]
-
棧block
a. 如果block是在棧上,將不會對auto變量產生強引用
b. 棧上的block隨時會被銷燬,也沒必要去強引用其他對象 -
堆block
a. 如果block被拷貝到堆上:- 會調用block內部的copy函數
- copy函數內部會調用_Block_object_assign函數
- _Block_object_assign函數會根據auto變量的修飾符(__strong、__weak、__unsafe_unretained)做出相應的操作,形成強引用(retain)或者弱引用
b. 如果block從堆上移除
- 會調用block內部的dispose函數
- dispose函數內部會調用_Block_object_dispose函數
- _Block_object_dispose函數會自動釋放引用的auto變量(release)
// - 測試 (一)
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
Person *person = [[Person alloc] init];
person.age = 10;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"age:%d",person.age);
});
NSLog(@"touchesBegan");
}
[打印] : touchesBegan age:10 Person-dealloc
// - 測試 (二)
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
Person *person = [[Person alloc] init];
person.age = 10;
__weak Person *weakPerson = person;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"age:%p",weakPerson);
});
NSLog(@"touchesBegan");
}
[打印] : touchesBegan Person-dealloc age:0x0
// - 測試 (三)
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
Person *person = [[Person alloc] init];
person.age = 10;
__weak Person *weakPerson = person;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.0 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"2-----age:%p",person);
});
NSLog(@"1-----age:%p",weakPerson);
});
NSLog(@"touchesBegan");
}
[打印] : touchesBegan 1-----age:0x604000015eb0 2-----age:0x604000015eb0 Person-dealloc
// - 測試 (四)
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
Person *person = [[Person alloc] init];
person.age = 10;
__weak Person *weakPerson = person;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.0 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"2-----age:%p",weakPerson);
});
NSLog(@"1-----age:%p",person);
});
NSLog(@"touchesBegan");
}
[打印] : touchesBegan 1-----age:0x6000000178d0 Person-dealloc 2-----age:0x0
- __block 修飾的變量
// - __block 的使用
int b = 20;
__block int age = 10;
MJBlock block = ^{
age = 20;
NSLog(@"age is %d", age);
};
struct __main_block_impl_0 *blockImpl = (__bridge struct __main_block_impl_0 *)block;
((void (*)(void *))blockImpl->impl.FuncPtr)(blockImpl);
// - __block 修飾的變量的內部的代碼 如下 age 被包裝成一個對象
struct __Block_byref_age_0 {
void *__isa;
struct __Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(void);
void (*dispose)(void);
};
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
struct __Block_byref_age_0 *age;
};
[__block 的使用] :
-
__block可以用於解決block內部無法修改auto變量值的問題(auto修飾變量,block無法修改,因爲block使用的時候是內部創建了變量來保存外部的變量的值,block只有修改內部自己變量的權限,無法修改外部變量的權限。)
-
__block不能修飾全局變量、靜態變量(static)(因爲全局變量, block 內部可以直接訪問到,不用捕獲; static 變量, 是指針傳遞, 也是可以直接修改的)
-
__block 修飾的變量, 內部的實質是將 block 內部要訪問的那個變量,包裝成爲一個對象, 這樣就可以隨意賦值了;
-
當block被copy到堆時,copy函數內部會調用_Block_object_assign函數, _Block_object_assign函數會對__block變量形成強引用(retain)對於__block 修飾的變量 assign函數對其強引用;對於外部對象 assign函數根據外部如何引用而引用;
-
block 的使用(少見的 block 的定義屬性的方法)
// - 定義 block
typedef QGLabel *(^Block3)(UIColor *color);
@interface QGLabel : UILabel
@property (nonatomic, copy) Block3 block2;
@property (nonatomic, copy) Block3 block3;
@property (nonatomic, copy) NSString *(^block)(NSNumber *);
@end
@implementation QGLabel
#pragma mark - 設置初始化數據
/** 設置數據 */
-(void)setupData{
self.h = ^NSString *(NSNumber *num) {
NSLog(@"%@", num);
};
__weak typeof(self) weakSelf = self;
self.block2 = ^QGLabel *(UIColor *color) {
weakSelf.textColor = color;
return weakSelf;
};
}
-(Block3)block3{
__weak typeof(self) weakSelf = self;
return ^QGLabel *(UIColor *color) {
weakSelf.backgroundColor = color;
return weakSelf;
};
}
/** 測試調用 */
-(void)test{
// - setter
self.block = ^NSString *(NSNumber *num) {
return [NSString stringWithFormat:@"%@", num];
};
// - getter
self.block(@(1));
}
/** block 的 setter */
-(void)setBlock:(NSString *(^)(NSNumber *num))block{
NSString *str = [NSString stringWithFormat:@"block 的 setter %@", block(@1)];
NSLog(@"str : %@", str);
}
/** block 的 getter */
-(NSString *(^)(NSNumber *))block{
return ^NSString *(NSNumber *num){
NSString *str = [NSString stringWithFormat:@"block 的 getter %@", num];
NSLog(@"str : %@", str);
return str;
};
}
@end
// - block中傳遞 self ,使self及時釋放
- (void)asyncRun:(void(^)(MLVBLiveRoom *self))block {
__weak __typeof(self) wself = self;
dispatch_async(_queue, ^{
__strong __typeof(wself) self = wself;
if (self) {
block(self);
}
});
}
block 中使用 __weak
// - 視圖控制器
- (void)viewDidLoad {
[super viewDidLoad];
self.redView = [[RedView alloc]initWithFrame:CGRectMake(90, 90, 90, 90)];
self.redView.backgroundColor = [UIColor redColor];
[self.view addSubview:self.redView];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self.redView removeFromSuperview];
self.redView = nil;
}
/* 以下代碼中 (在 property的聲明中可以不用__weak 修飾)
// - 聲明中可以不寫 __weak; 即:
@property (nonatomic, copy) void(^block)( id xx); 但是後邊的賦值,必須寫__weak,否則不會及時釋放
@property (nonatomic, copy) void(^block)(__weak id xx);
self.block = ^(__weak id xx) {
[NSTimer scheduledTimerWithTimeInterval:10 repeats:NO block:^(NSTimer * _Nonnull timer) {
NSLog(@"xx : %@", xx);
}];
};
*/
1. 如果 block 中的 xx 變量, 如果沒有使用__weak 修飾, touch時候, 則不會立即 dealloc ,需要計時器 10s 後回調block打印 xx : <RedView: 0x7faabc411830; frame = (90 90; 90 90); layer = <CALayer: 0x6000028d8920>> 之後纔會 dealloc
2. 如果 block 中的 xx 變量, 如果有__weak 修飾, touch時候, 會立即 dealloc ,然後計時器 10s 後打印 xx : (null)
// - redView
@interface RedView()
@property (nonatomic, copy) void(^block)(__weak id xx);
@end
@implementation RedView
- (instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
self.block = ^(__weak id xx) {
[NSTimer scheduledTimerWithTimeInterval:10 repeats:NO block:^(NSTimer * _Nonnull timer) {
NSLog(@"xx : %@", xx);
}];
};
self.block(self);
}
return self;
}
-(void)dealloc{
NSLog(@"dealloc --- [%s]", __func__);
}
@end