Objective-C当中的Block
Objective-C当中的Block
Blocks是C语言的扩充功能,即带有自动变量(局部变量)的匿名函数。 使用clang可以通过添加-rewrite-objc编译选项将含有Block语法的源代码变换为C++的源代码,如用clang -rewrite-objc main.m会生成一个与main.m对等的main.cpp文件。
先看总结:
- 当没有引入外部自动变量时,
Block是__NSGlobalBlock__; - 当只引入了非自动变量(如
void *、变量的类型)时,Block仍然是__NSGlobalBlock__; - 当引入了外部自动变量时,
Block会变成__NSMallocBlock__; - 用
__weak修饰之后,Block变成了__NSStackBlock__; - 引入静态变量、全局变量或全局静态变量,
Block是__NSGlobalBlock__; Block截获的自动变量值被保存在Block结构体实例(即Block自身)中,即__main_block_impl_0结构体中;Block优先设置在__NSStackBlock__上,只有当Block引入的变量的作用域结束时,才会把Block及__block变量从栈复制到堆上,同时栈上的Block与__block变量被废弃,堆上的不受影响;__NSGlobalBlock__类型的Block是存放在全局数据区的,只要Block不截获自动变量,那Block结构体实例就会存放在全局数据区,即程序的__DATA__段中。-
当通过函数返回的
Block,函数返回时编译器会自动生成复制到堆上的代码objc_retainBlock,该函数内直接调用了_Block_copy,而此函数就会将Block从栈上复制到堆上; Block结构体类似于基于objc_object结构体的Ojbective-C类对象的结构体,其中的成员变量isa在构造函数中是这样isa = &_NSConcreteStackBlock;被初始化的。

blk()的调用,实际上是调用__main_block_impl_0里__block_impl实例impl的成员FuncPtr,而FuncPtr即是构造Block对象(__main_block_impl_0)时传入的__main_block_func_0函数指针;
-
__main_block_copy_0与__main_block_dispose_0是拷贝和释放赋值在对象类型的结构体成员变量中的对象,在Block从栈复制到堆时以及堆上的Block被废弃时会调用这两函数; -
当变量用
__block修饰变量a后,在block捕获变量a后会为变量生成一个叫__Block_byref_a_0的__block结构体,用来存放a变量,并且在__main_block_impl_0的结构体中引用__Block_byref_a_0对象的指针作为结构体的成员变量。
- 以下几种情况栈上的
Block会复制到堆上:- 调用
Block的copy实例方法; Block作为函数返回值返回时;- 将
Block赋值给附有__strong修饰的id类型变量或Block类型成员变量时; - 在方法名中含有
usingBlock的cocoa框架方法或CGD的API中传递Block时;
- 调用
编译源码libclosure
-
把
Base SDK设置为macOS;
- 从Apple Source code下载源码;
-
当编译源码
libclosure报<platform/string.h> file not found时,把libplatform下载解压后,把头文件导入macosx的sdk的路径下的/usr/local/include下,使用如下命令:sudo ditto $PWD/private $(xcrun -sdk macosx -show-sdk-path)/usr/local/include //查看`macosx`的`sdk`的路径:`xcrun -sdk macosx -show-sdk-path`; -
同理,如果报
<os/assumes.h> file not found,把Libc下下来并把其中os下的头文件拷贝到$(xcrun -sdk macosx -show-sdk-path)/usr/local/include/os/下。 -
如果报*included file ‘
/Makefiles/CoreOS/Xcode BSD.xcconfig does not exist*,就把[ CoreOSMakefiles](https://luowei.github.io/https://opensource.apple.com/tarballs/CoreOSMakefiles/CoreOSMakefiles-79.tar.gz)下载下来解压后,把里面的`BSD.xcconfig`拷贝到`/Applications/Xcode.app/Contents/Developer`下。 - 当然更好的方式,是在项目target的 Build Settings -> Header Search Paths 里添加,如这样:
$(SRCROOT)/LWCommon //自己创建的文件夹,放缺失的头文件 $(DSTROOT)/usr/include $(DSTROOT)/usr/local/include $(CONFIGURATION_BUILD_DIR)/usr/include $(CONFIGURATION_BUILD_DIR)/usr/local/include /System/Library/Frameworks/System.framework/PrivateHeaders - 源码编译成功后,建立新的 target 来执行你程序,从而跑进源码,在target上设置target -> objc -> Build Settings -> Enable Hardened Runtime -> NO 即可。
下面用代码来验证这些结论:
#import <Foundation/Foundation.h>
#pragma clang diagnostic push
@interface Hello : NSObject
-(NSString *)uname;
@end
@implementation Hello
-(NSString *)uname{
return @"张三";
}
@end
typedef int(^Block_t) (int);
//1. 当没有引入外部自动变量时,blk是__NSGlobalBlock__
void testBlock1() {
void (^blk)(void) = ^{
NSLog(@"Hello");
};
blk();
NSLog(@"====blk1:%@",blk);
// for(int i=0;i<10;i++){
// Block_t blk_t = ^(int count){return count * i;};
// blk_t(5);
// NSLog(@"====blk_t1:%@",blk_t);
// }
}
//2. 当引入了非自动变量(如void*、变量的类型)时,blk还是__NSGlobalBlock__
void testBlock2() {
//void *hello = (__bridge void *)([[Hello alloc] init]);
Hello *hello = [[Hello alloc] init];
//分配1个32字节的空间
void *p1 = calloc(1, 32);
void *p2 = calloc(1, 16);
void (^blk)(void) = ^{
NSLog(@"hello size:%lu",sizeof(hello)); //这样引入就还是__NSGlobalBlock__
// NSLog(@"hello:%@",hello); //这样引入就变成了__NSMallocBlock__
NSLog(@"p1:%lu p2:%lu",sizeof(p1),sizeof(p2));
// 如果此时调用free释放掉p1,p2,则相当于引入了自动变量
// free(p1);
// free(p2);
};
blk();
NSLog(@"====blk2:%@",blk);
}
//3. 当引入了外部自动变量时,blk会变成__NSMallocBlock__
void testBlock3() {
int a = 10;
void (^blk)(void) = ^{
NSLog(@"a:%i",a);
};
blk();
NSLog(@"====blk3:%@",blk);
}
#pragma clang diagnostic ignored "-Warc-unsafe-retained-assign"
//4.用__weak修饰之后,变成了__NSStackBlock__
void testBlock4() {
int a = 10;
void (__weak ^blk)(void) = ^{
NSLog(@"a:%i",a);
};
blk();
NSLog(@"====blk4:%@",blk);
}
//5.引入静态变量、全局变量或全局静态变量,是__NSGlobalBlock__
int global_var = 1;
static int static_global_var = 2;
void testBlock5() {
//静态变量
static int static_var = 3;
void (^blk)(void) = ^{
global_var *= 3;
static_global_var *= 3;
static_var *= 3;
NSLog(@"global_var:%i",global_var);
NSLog(@"static_global_var:%i",static_global_var);
NSLog(@"static_var:%i",static_var);
};
blk();
NSLog(@"====blk5:%@ static_var:%i",blk,static_var);
}
//6.通过函数返回的block,函数返回时编译器会自动生成复制到堆上的代码 objc_retainBlock,
// 该函数内直接调用了_Block_copy,将Block复制到堆上
Block_t getBlock(int rate){
return ^(int count){ return rate * count; };
}
void testBlock6(){
Block_t blk = getBlock(3);
int count = blk(2);
NSLog(@"====blk6:%@ returnValue:%i",blk,count);
}
//7.block存放到复数,会自动复制到堆上
NSArray* getBlockList(){
int a = 10;
return @[
^{NSLog(@"block list 0:%i",a);},
^{NSLog(@"block list 1:%i",a);},
];
}
void testBlock7(){
NSArray *blockList = getBlockList();
typedef void(^blk_t)(void);
blk_t blk = (blk_t)blockList.firstObject;
blk();
NSLog(@"====blk7:%@",blk);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
testBlock1();
testBlock2();
testBlock3();
testBlock4();
testBlock5();
testBlock6();
testBlock7();
}
return 0;
}
#pragma clang diagnostic pop
输出:
hello_block[56269:5472036] Hello
hello_block[56269:5472036] ====blk1:<__NSGlobalBlock__: 0x100399050>
hello_block[56269:5472036] hello size:8
hello_block[56269:5472036] p1:8 p2:8
hello_block[56269:5472036] ====blk2:<__NSGlobalBlock__: 0x100399070>
hello_block[56269:5472036] a:10
hello_block[56269:5472036] ====blk3:<__NSMallocBlock__: 0x7fa50ec06440>
hello_block[56269:5472036] a:10
hello_block[56269:5472036] ====blk4:<__NSStackBlock__: 0x7ffeef867f98>
hello_block[56269:5472036] global_var:3
hello_block[56269:5472036] static_global_var:6
hello_block[56269:5472036] static_var:9
hello_block[56269:5472036] ====blk5:<__NSGlobalBlock__: 0x1003990b0> static_var:9
hello_block[56269:5472036] ====blk6:<__NSMallocBlock__: 0x7fa50ef04470> returnValue:6
hello_block[56269:5472036] block list 0:10
hello_block[56269:5472036] ====blk7:<__NSMallocBlock__: 0x7fa50ef04790>
