Objective-C:Block
Objective-C中的匿名函数–Blcok
Block定义 及安全调用
BlockTips 演示类 interface
~~~objective-c
@interface BlockTips : NSObject
//@property (nonatomic, strong) void(^aBlock)(void);
/** 1.利用系统所有的type定义无参无返回值的block*/
@property (nonatomic, strong) dispatch_block_t aBlock;
/**
* 2.block属性安全调用
*/
- (void)blockHandleSafely;
@end
~~~
安全调用:
~~~objective-c
@implementation BlockTips
/**
* 2.block属性安全调用
*/
- (void)blockHandleSafely {
!_aBlock ? : _aBlock();
}
@end
~~~
weak- strong-dance
测试控制器接口:
~~~objective-c
@interface BlockTest ()
{
int _number;
}
@property (nonatomic, strong) BlockTips * blockTips;
@end
~~~
创建tips对象:
~~~objective-c
self.blockTips = [BlockTips new];
~~~
weak-strong-dance:
~~~objective-c
// 存在使用了类的成员变量而导致循环引用的情况,需要使用 weak- strong-dance,并在访问实例变量使用结构体访问成员的操作符 ->
__weak typeof(self) weakSelf = self;
self.blockTips.aBlock = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) {
return ;
}
strongSelf->_number = 1024;
};
// block属性安全调用
[self.blockTips blockHandleSafely];
NSLog(@"self _number is : %d", _number);
~~~
Block 实现函数多返回值
~~~objective-c
/**
* block实现函数多返回值
*/
- (void)calculateA:(int)a B:(int)b Result:(void(^)(int sum, int max, int min))result {
if (!result) {
return;
}
result(a + b, MAX(a, b), MIN(a, b));
}
/**
* make test
*/
- (void)blockReturnValuesInFunctions {
int a = 1;
int b = 2;
__block int _sum = 0;
__block int _min = 0;
__block int _max = 0;
[self calculateA:a B:b Result:^(int sum, int max, int min) {
_sum = sum;
_min = min;
_max = max;
}];
NSLog(@"sum is : %d, min is : %d, max is : %d", _sum, _min, _max);
}
~~~
Block实现链式语法
BlockLink 演示类 interface:
~~~objective-c
@interface BlockLink : NSObject
{
NSInteger _money;
}
@property (nonatomic, strong, readonly) BlockLink * (^add)(NSInteger number);
@property (nonatomic, strong, readonly) BlockLink * (^subtract)(NSInteger number);
- (NSInteger)currentMoney;
@end
~~~
BlockLink 演示类 implementation:
~~~objective-c
@implementation BlockLink
- (NSInteger)currentMoney {
return _money;
}
- (BlockLink *(^)(NSInteger))add {
return ^ BlockLink * (NSInteger number) {
_money += number;
return self;
};
}
- (BlockLink *(^)(NSInteger))subtract {
return ^ BlockLink * (NSInteger number) {
_money -= number;
return self;
};
}
@end
~~~
调用:
~~~objective-c
// block实现链式语法
BlockLink * link = [BlockLink new];
NSLog(@"current money is : %zd", [link currentMoney]);
link.add(100).subtract(10).add(5).subtract(20);
NSLog(@"current money is : %zd", [link currentMoney]);
~~~
Block的底层实现
NSConcerteGlobalBlock
我们在ARC环境中新建命令行项目,在main.m中编写一个block:
~~~objective-c
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
^{
printf("hello world");
}();
}
return 0;
}
~~~
用clang将main.m编译成cpp文件看一下底层实现(屏蔽无关代码):
~~~objective-c
#ifndef BLOCK_IMPL
#define BLOCK_IMPL
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;
__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;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("hello world");
}
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;
((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA))();
}
return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
~~~
可以看到__main_block_impl_0就是该block的实现,其内构成:
- impl:block_impl结构体,impl是实际的函数指针,指向 main_block_func_0
- Desc:__main_block_desc_0结构体,存储block附加信息,这里主要是结构体大小
- impl.isa = &_NSConcreteStackBlock; 由于clang和LLVM改写差异,在LLVM中应该是NSConcreteGlobalBlock。
NSConcerteStackBlock
不加 _ _block 的实现:
~~~objective-c
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
int a = 10;
^{
printf("hello world %d", a);
}();
}
return 0;
}
~~~
用clang重编译:
~~~objective-c
#ifndef BLOCK_IMPL
#define BLOCK_IMPL
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 a;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, 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) {
int a = __cself->a; // bound by copy
printf("hello world %d", a);
}
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 a = 10;
((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a))();
}
return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
~~~
- 这里,impl.isa = &_NSConcreteStackBlock; isa指向NSConcerteStackBlock;
- main_block_impl_0中增加了一个变量a,在block中引用的变量a实际是在声明block时,被复制到 main_block_impl_0 的结构体中;这样,在block内部修改变量a,不会影响到外部的实际变量;
- main_block_impl_0中由于增加了变量a,所以结构体大小变大了,改结构体大小被写在了 main_block_desc_0中。
加 _ _block 的实现:
~~~objective-c
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
__block int a = 10;
^{
printf("hello world %d", a);
}();
}
return 0;
}
~~~
用clang重编译:
~~~objective-c
#ifndef BLOCK_IMPL
#define BLOCK_IMPL
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
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;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_a_0 *a = __cself->a; // bound by ref
printf("hello world %d", (a->__forwarding->a));
}
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, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
__attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10};
((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344))();
}
return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
~~~
- 源码中多了一个__Block_byref_a_0类型的结构体,用来保存我们capture的变量a;
- main_block_impl_0中引用的是Block_byref_a_0的结构体指针a,这样就可以达到访问外部变量的作用;
- Block_byref_a_0结构体中带有isa,说明它也是一个对象;
- 该block负责Block_byref_a_0结构体相关的内存管理,main_block_desc_0中多了copy和dispose函数,用于修改引用计数。
我们观察Block_byref_a_0中的__forwarding指针:
~~~objective-c
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding;
int __flags;
int __size;
int a;
};
~~~
分配在栈上的Block,其所属的变量的作用域结束后。改Block就会被废弃,block变量也分配在栈上,当超过该变量的作用域时,该block变量也会被废弃。 将Block从栈复制到堆上,block变量也被复制到了堆栈,堆上的block变量被堆上的Block持有,在这一变化后,**forwarding指针保证了**block变量的正确访问。
即,栈上的block变量被复制到了堆上,其forwarding指针的指向从指向自身,变为指向堆中的__block变量的结构体:
~~~objective-c
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_a_0 *a = __cself->a; // bound by ref
printf("hello world %d", (a->__forwarding->a));
}
~~~
通过cself找到变量val,再通过val的forwarding找到真正的val变量。
变量的复制
- 对于block外的变量引用,block是将其复制到block的数据结构中来访问的;
- 对于__block修饰的外部变量,block是复制其引用地址来访问的;
- 对于数组,block复制其本身的地址,而指向的地址是相同的(数组首地址),所以不需要__block修饰。
~~~objective-c
NSMutableArray * array = @[].mutableCopy;
NSLog(@"%p %@", &array, array);
^{
[array addObject:@"1"];
[array addObject:@"3"];
NSLog(@"%p %@", &array, array);
}();
NSLog(@"%p %@", &array, array);
~~~
打印结果:
~~~objective-c
2017-11-27 09:17:51.169619+0800 Testsss[1624:256717] 0x7fff57a443c8 (
)
2017-11-27 09:17:51.170120+0800 Testsss[1624:256717] 0x7fff57a443c0 (
1,
3
)
2017-11-27 09:17:51.170305+0800 Testsss[1624:256717] 0x7fff57a443c8 (
1,
3
)
~~~
MRC中的Block
从捕获变量的角度来分类,MRC中的Block有三种:
- NSConcreteStackBlock:只访问局部变量,成员变量,没有强指针引用。在函数返回后即销毁;
- NSConcreteMallocBlock:有强指针引用或者copy修饰,block被复制到堆中,没有强指针引用即销毁;
- NSConcreteGlobalBlock:没有访问外界变量,或只访问全局变量、静态变量,应用程序结束才销毁。
ARC中的Block
ARC中,将只有NSConcerteGlobalBlock和NSConcreteMallocBlock;
NSConcreteStackBlock类型会被NSConcreteMallocBlock替代;
由于ARC已经能很好的处理对象的生命周期的管理,这样所有对象都放到堆上管理,对编译器来说,实现比较方便。
最后,参考链接:http://blog.devtang.com/2013/07/28/a-look-inside-blocks/
感谢该博主的文章。