博客連接Blocks原理ios
更新日期:2019-07-22面試
咱們先寫一個最基礎的block編程
int main(int argc, const char * argv[]) {
void (^testBlock)(void) = ^{
printf("asddasd");
};
testBlock();
return 0;
}
複製代碼
使用xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
轉化成C++代碼。其中有關鍵代碼以下:api
int main(int argc, char * argv[]) {
void (*testBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock);
return 0;
}
複製代碼
將代碼簡化一下:bash
int main(int argc, char * argv[]) {
// 定義block變量
void (*testBlock)(void) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
// 執行block
testBlock->FuncPtr(testBlock);
return 0;
}
複製代碼
能夠看出咱們定義的testBlock
變成了&__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA)
,執行block的時候將testBlock()
變成了testBlock->FuncPtr(testBlock)
框架
__main_block_impl_0
是一個結構體,相關定義以下:iphone
struct __block_impl {
void *isa; // isa是OC對象特有的標誌
int Flags;
int Reserved;
void *FuncPtr; // 該指針指向block執行函數的地址
};
static struct __main_block_desc_0 {
size_t reserved; // 預留的字段
size_t Block_size; // block所佔內存大小
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
// __main_block_impl_0是一個C++結構體能夠聲明函數
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; // 初始化__block_impl結構體的isa成員變量
impl.Flags = flags;
impl.FuncPtr = fp; // fp就是__main_block_func_0函數
Desc = desc;
}
};
// 該函數封裝了block的執行代碼
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("asddasd");
}
複製代碼
__main_block_impl_0
有兩個成員變量,分別是__block_impl impl
和__main_block_desc_0* Desc
,還有一個__main_block_impl_0
的構造函數。__block_impl
和__main_block_desc_0
也是兩個結構體,其成員變量做用都寫在代碼註釋中,這裏就再也不說了。函數
最重要的是其構造函數:測試
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
複製代碼
在定義block變量的時候,傳入的__main_block_func_0
和&__main_block_desc_0_DATA
分別對應了fp
和* desc
。fp
指向的就是__main_block_func_0
函數,它會傳給impl.FuncPtr
,另外的isa
的初始值是_NSConcreteStackBlock
,_NSConcreteStackBlock
就至關於class_t
中的結構體類型的。優化
接着看調用部分,關於block的調用會被轉化成testBlock->FuncPtr(testBlock)
,它指向的是__main_block_func_0
函數的地址。這裏要注意testBlock->FuncPtr(testBlock)
是我將強制轉化取消後的代碼,本來應該是(__block_impl *)testBlock->FuncPtr(testBlock)
,由於__block_impl imp
是__main_block_impl_0
這個結構體的第一個成員變量,因此__block_impl imp
的內存地址就是__main_block_impl_0
結構體的內存地址。
因此說block的本質就是Objective-C對象,block的調用就是函數指針的調用。
根據《Objective-C高級編程》一書中提到,所謂的「截獲自動變量值」意味着在執行Block語法時,Block語法表達式所使用的自動變量被保存到Block的結構體實例(即Block自身中)。
那不一樣類型變量之間的截獲會有區別嗎,使用如下代碼:
#include <stdio.h>
static int globalStaticValue = 1;
int globalValue = 2;
int main(int argc, char * argv[]) {
int a = 3;
static int b = 4;
void (^testBlock)(void) = ^{
printf("%d", globalStaticValue);
printf("%d", globalValue);
printf("%d", a);
printf("%d", b);
};
testBlock();
return 0;
}
複製代碼
轉化成C++代碼:
static int globalStaticValue = 1;
int globalValue = 2;
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int a;
int *b;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int *_b, int flags=0) : a(_a), b(_b) {
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 = __cself->a; // bound by copy
int *b = __cself->b; // bound by copy
printf("%d", globalStaticValue);
printf("%d", globalValue);
printf("%d", a);
printf("%d", (*b));
}
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, char * argv[]) {
int a = 3;
static int b = 4;
void (*testBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a, &b));
((void (*)(__block_impl *))((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock);
return 0;
}
複製代碼
從上面的代碼能夠看出:
block是如何截獲對象變量的,使用以下代碼:
#import <Foundation/Foundation.h>
static NSObject *globalObjc;
int main(int argc, char * argv[]) {
NSObject *object = [[NSObject alloc] init];
__weak NSObject *weakObject = object;
void (^testBlock)(void) = ^{
NSLog(@"%@", object);
NSLog(@"%@", weakObject);
NSLog(@"%@", globalObjc);
};
testBlock();
return 0;
}
複製代碼
使用xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
命令,轉化成C++代碼後:
static NSObject *globalObjc;
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
NSObject *__strong object;
NSObject *__weak weakObject;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *__strong _object, NSObject *__weak _weakObject, int flags=0) : object(_object), weakObject(_weakObject) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSObject *__strong object = __cself->object; // bound by copy
NSObject *__weak weakObject = __cself->weakObject; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_2p_n_j9tbqx6lqb2bqv110q6d300000gn_T_main_a7d9fa_mi_0, object);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_2p_n_j9tbqx6lqb2bqv110q6d300000gn_T_main_a7d9fa_mi_1, weakObject);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_2p_n_j9tbqx6lqb2bqv110q6d300000gn_T_main_a7d9fa_mi_2, globalObjc);
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->object, (void*)src->object, 3/*BLOCK_FIELD_IS_OBJECT*/);
_Block_object_assign((void*)&dst->weakObject, (void*)src->weakObject, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->object, 3/*BLOCK_FIELD_IS_OBJECT*/);
_Block_object_dispose((void*)src->weakObject, 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};
int main(int argc, char * argv[]) {
NSObject *object = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
__attribute__((objc_ownership(weak))) NSObject *weakObject = object;
void (*testBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, object, weakObject, 570425344));
((void (*)(__block_impl *))((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock);
return 0;
}
複製代碼
若是截獲的變量是對象類型的,會將對象變量及其全部權修飾符一併截獲。再看一下__main_block_copy_0
和__main_block_dispose_0
。
當block進行一次copy操做的時候,__main_block_copy_0
函數內部調用_Block_object_assign
函數,它根據對象的類型產生強引用或者弱引用;
當block從堆中移除的時候,__main_block_dispose_0
函數內部調用_Block_object_dispose
函數,它會自動釋放掉引用的變量。
關於截獲對象的實驗是在ARC環境下執行的,testBlock是_NSConcreteMallocBlock
類型的。若是在非ARC環境下,testBlock是_NSConcreteStackBlock
類型的即Block在棧上,則不會對截獲的對象變量進行強引用。
當咱們須要對被Block截獲的局部變量進行賦值操做的話,須要添加一個__block
這個說明符,那__block
到底有什麼做用呢?
先看一段代碼:
#include <stdio.h>
int main(int argc, char * argv[]) {
__block int a = 1;
void (^testBlock)(void) = ^{
a = 100;
printf("%d", a);
};
testBlock();
return 0;
}
複製代碼
因爲使用了__block
關鍵字,能夠修改變量a的值,因此輸出結果是100。
先說明一下爲何不使用__block
就不能修改值。執行block就是調用__main_block_func_0
函數,a
是main
函數中的局部變量,咱們不可能在一個函數中去修改另外一個函數的局部變量。
將上面代碼轉換成C++代碼,先看一下__main_block_impl_0
這個結構體
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding;
int __flags;
int __size;
int a;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_a_0 *a; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
複製代碼
__main_block_impl_0
結構體中多了一個__Block_byref_a_0 *a
而不是int a
。__Block_byref_a_0
也是一個結構體,裏面也有一個isa
指針,所以咱們也能夠將它當作一個對象。另外還有一個__Block_byref_a_0 *
類型的__forwarding
指針和變量a
。關於__forwarding
指針的做用咱們會在後面提到。
接着是主函數:
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
}
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};
int main(int argc, char * argv[]) {
__attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 1};
void (*testBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));
((void (*)(__block_impl *))((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock);
return 0;
}
複製代碼
在主函數中咱們能夠看到__block int a = 1;
轉化成了__Block_byref_a_0
的一個結構體,其中__forwarding
指針指向這個結構體本身。
最後是Block執行的時候即調用__main_block_func_0
函數:
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_a_0 *a = __cself->a; // bound by ref
(a->__forwarding->a) = 100;
printf("%d", (a->__forwarding->a));
}
複製代碼
原來的a = 100;
則被轉換成__Block_byref_a_0 *a = __cself->a;
和(a->__forwarding->a) = 100;
。這兩句代碼的意思是先獲取結構體中的a
,經過a
結構體的__forwarding
指針指向成員變量a
賦值爲100。
因此__block
的本質就是**__block將變量包裝成一個對象,將截獲到的值存在這個對象中,經過對截獲的值進行賦值而更改原有的值**。
block有下面三種類型
類 | 設置對象的存儲域 |
---|---|
_NSConcreteStackBlock | 棧 |
_NSConcreteGlobalBlock | 程序的數據區域 |
_NSConcreteMallocBlock | 堆 |
那麼如何肯定block的類型?下面這個實驗須要在MRC環境下進行測試,代碼以下:
int main(int argc, char * argv[]) {
int a = 1;
void (^block1)(void) = ^{
NSLog(@"%s - %d", __func__, a);
};
void (^block2)(void) = ^{
NSLog(@"%s", __func__);
};
void (^block3)(void) = [block1 copy];
NSLog(@"%@ %@ %@", [block1 class], [block2 class], [block3 class]);
return 0;
}
複製代碼
結果以下:
經過打印block的類型能夠知道
類 | 環境 |
---|---|
_NSConcreteStackBlock | 訪問了自動變量值 |
_NSConcreteGlobalBlock | 沒有訪問自動變量值 |
_NSConcreteMallocBlock | _NSConcreteStackBlock使用了copy |
copy操做對不一樣類型block的影響:
Block的類 | 副本源的配置存儲域 | 複製效果 |
---|---|---|
_NSConcreteStackBlock | 棧 | 從棧複製到堆 |
_NSConcreteGlobalBlock | 程序的數據區域 | 什麼也不作 |
_NSConcreteMallocBlock | 堆 | 引用計數增長 |
block的copy操做能保證block不容易被銷燬。可是在ARC環境下,編譯器會根據狀況自動將棧上的block複製到堆上,即咱們打印出來的block是_NSConcreteMallocBlock
類型的。
這些狀況有:
因此若是將上面代碼放在ARC環境下執行的話,則block1
是__NSMallocBlock__
類型的。
release操做會在其內部自動執行。
__block變量的存儲域 | Block從棧複製到堆時的影響 |
---|---|
棧 | 從棧複製到堆並被Block持有 |
堆 | 被Block持有 |
既然__block
對應的結構體或者對象是在Block內部使用,那麼Block就須要對這個對象的生命週期進行負責。接着咱們從源碼裏面進行理解
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
}
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};
int main(int argc, char * argv[]) {
__attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 1};
void (*testBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));
((void (*)(__block_impl *))((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock);
return 0;
}
複製代碼
咱們須要注意兩個函數__main_block_copy_0
和__main_block_dispose_0
。
__main_block_copy_0
函數內部調用_Block_object_assign
函數,它會對__block
變量生成強引用;__main_block_dispose_0
函數內部調用_Block_object_dispose
函數,它會自動釋放掉引用的__block
變量。上面的代碼是在ARC環境下的,若是Block在棧上,並不會對__block
變量產生引用。
這裏還要解決一個前面遺留的問題,既然__Block_byref_a_0
結構體中已經有變量a
,爲何還須要使用__forwarding
指針對a
賦值呢或者說__forwarding
存在的意義是什麼?
關於__forwarding
的做用《Objective-C高級編程》一書中的第111頁、第112頁中有很明確的說到:__block變量的結構體成員變量__forwarding能夠實現不管__block變量配置在棧上仍是堆上都可以正確地訪問__block變量。
沒有進行copy操做的時候:
這裏分爲兩種狀況:
若是咱們沒有對棧上的Block執⾏copy操做,修改被__block
修飾的變量其實是經過其__forwarding
指針指向的自身,對其中的變量進行修改。
若是咱們執行過copy操做,那麼棧上的Block的__forwarding
指針,實際是指向堆上的__block
修飾的變量,⽽堆上的__forwarding
指針則指向⾃自身的__block
修飾的變量。在變量做用域結束的時候,棧上的__block
變量和Block被廢棄掉,可是堆上的__block
變量和Block不受影響。
咱們在使用Block的時候,若是有一個對象持有了這個Block,而在Block內部又使用了這個對象,就會形成循環引用。如如下寫法:
#import <Foundation/Foundation.h>
typedef void (^TestBlock) (void);
@interface AObject: NSObject {
TestBlock testBlock;
}
@end
@implementation AObject
- (instancetype)init {
if (self = [super init]) {
testBlock = ^{
NSLog(@"self = %@", self);
};
}
return self;
}
- (void)dealloc {
NSLog(@"%@ dealloc", self.class);
}
@end
int main(int argc, char * argv[]) {
AObject *a = [[AObject alloc] init];
NSLog(@"%@", a);
return 0;
}
複製代碼
能夠看到,上面這段代碼發生了循環引用,致使AObject對象沒法被釋放。一般作法是使用__weak
關鍵字在Block外部聲明一個弱引用。從新修改下init
方法:
- (instancetype)init {
if (self = [super init]) {
__weak AObject *weakSelf = self;
testBlock = ^{
NSLog(@"self = %@", weakSelf);
};
}
return self;
}
複製代碼
前面在講述截獲自動變量值的時候,咱們知道Block在截獲對象類型的時候,會連同對象的全部權修飾符一塊兒截獲,這裏截獲的是__weak
修飾的weakSelf
,所以不會發生循環引用。
在爲避免循環引用而使用__weak
修飾符的時候,還要注意有可能在Block執行的時候,對象在中途被釋放掉了。這個時候須要在Block內部聲明一個局部變量強持有對象,這個局部變量會在到Block執行結束時自動釋放,不會形成循環引用,而對象也會在Block執行結束後被釋放。
是否是全部的Block都須要在外部聲明使用__weak
修飾呢?答案是否認的。所謂「引用循環」是指雙向的強引用,因此那些「單向的強引用」(block強引用self)沒有問題。例如:
[UIView animateWithDuration:duration
animations:^{
[self.superview layoutIfNeeded];
}];
複製代碼
但若是你使用一些參數中可能含有ivar的系統的api,如NSNotificationCenter
就要當心一點。
__weak __typeof__(self) weakSelf = self;
_observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"testKey"
object:nil
queue:nil
usingBlock:^(NSNotification *note) {
//使用self會循環引用,可是使用weakSelf或者strongSelf則沒有問題
__typeof__(self) strongSelf = weakSelf;
[strongSelf dismissModalViewControllerAnimated:YES];
}];
複製代碼
能夠看出self --> _observer --> block --> self
這顯然是一個循環引用。總而言之,只有當self直接或間接的持有 Block,而且在Block內部又使用了self的時候,才應該使weakSelf。
copy
函數,對截獲的變量進行強/弱引用;dispose
函數,自動釋放引用的變量;代碼以下:
#import <Foundation/Foundation.h>
#import "fishhook.h"
// Block定義
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0 *Desc;
};
#pragma mark - 第一題
static void test_1__main_block_func_0(struct __main_block_impl_0 *__cself){
NSLog(@"Hello,world!");
}
void HookBlockToPrintHelloWorld(id block){
// struct __block_impl *tmp = (__bridge struct __block_impl *)block;
// tmp->FuncPtr = &test_1__main_block_func_0;
// 或者
struct __main_block_impl_0 *tmp2 = (__bridge struct __main_block_impl_0 *)block;
((struct __block_impl *)tmp2)->FuncPtr = &test_1__main_block_func_0;
}
#pragma mark - 第二題
static void (*original_func)(struct __main_block_impl_0 *, int, NSString *);
static void test_2__main_block_func_0(struct __main_block_impl_0 *__cself, int a, NSString *b) {
NSLog(@"%d %@", a, b);
original_func(__cself, a, b);
}
void HookBlockToPrintArguments(id block) {
struct __block_impl *tmp = (__bridge struct __block_impl *)block;
original_func = tmp->FuncPtr;
tmp->FuncPtr = &test_2__main_block_func_0;
}
#pragma mark - 第三題
static id (*original__Block_copy)(id);
id new__Block_copy(id block) {
id tmp = original__Block_copy(block);
HookBlockToPrintArguments(tmp);
return tmp;
}
void HookEveryBlockToPrintArguments() {
struct rebinding open_rebinding = {
"_Block_copy",
new__Block_copy,
(void *)&original__Block_copy
};
rebind_symbols((struct rebinding[1]){open_rebinding}, 1);
}
int main(int argc, char * argv[]) {
// 第一題
void(^block1)(void) = ^{
NSLog(@"%s", __func__);
};
HookBlockToPrintHelloWorld(block1);
block1();
// 第二題
void (^block2)(int a, NSString *b) = ^(int a, NSString *b) {
NSLog(@"block invoke");
};
HookBlockToPrintArguments(block2);
block2(123, @"aaa");
// 第三題
HookEveryBlockToPrintArguments();
void (^block3)(int a, NSString *b) = ^(int a, NSString *b) {
NSLog(@"block3 invoke");
};
block3(1, @"dsadas");
void (^block4)(int a, NSString *b) = ^(int a, NSString *b) {
NSLog(@"block4 invoke");
};
block4(222, @"ddd");
return 0;
}
複製代碼
第一題分析
想要替換原有的block實現,咱們須要知道Block是如何執行代碼的。經過上面的原理分析,咱們知道在執行block的代碼就至關於調用__main_block_func_0
函數。該函數指針被保存在__block_impl
結構體的FuncPtr
中。咱們知道將FuncPtr
指向咱們本身的函數就能夠完成切換。
第二題分析
知道第一題如何解答以後,第二題相對來講簡單一點,其實就是在咱們自定義函數的時候,這個函數須要包括咱們傳入的參數,以及原有實現。
那麼經過一個函數指針能夠保存原有實現,在自定義函數中經過函數指針調用原有函數便可,而參數是自己__main_block_func_0
函數就有的,咱們只要確保咱們自定義函數的參數列表與__main_block_func_0
函數的參數列表一致就行。
第三題分析
這道題目說白了就是在一個適當的時機去調用HookBlockToPrintArguments
。經過fishhook這個框架,咱們能夠動態的修改C語言函數。那如何肯定這個C語言函數呢?這個函數須要有這麼一個功能,它能夠拿到原來的block對象,通過替換再從新返回。
在ARC環境下,編譯器會根據狀況自動將棧上的block複製到堆上,它會調用一個copy
函數即_Block_copy
,因此咱們須要動態修改這個函數。