Objective-C:Block

Page content

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的实现,其内构成:

  1. impl:block_impl结构体,impl是实际的函数指针,指向 main_block_func_0
  2. Desc:__main_block_desc_0结构体,存储block附加信息,这里主要是结构体大小
  3. 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 };
~~~
  1. 这里,impl.isa = &_NSConcreteStackBlock; isa指向NSConcerteStackBlock;
  2. main_block_impl_0中增加了一个变量a,在block中引用的变量a实际是在声明block时,被复制到 main_block_impl_0 的结构体中;这样,在block内部修改变量a,不会影响到外部的实际变量;
  3. 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 };
~~~
  1. 源码中多了一个__Block_byref_a_0类型的结构体,用来保存我们capture的变量a;
  2. main_block_impl_0中引用的是Block_byref_a_0的结构体指针a,这样就可以达到访问外部变量的作用;
  3. Block_byref_a_0结构体中带有isa,说明它也是一个对象;
  4. 该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变量被复制到了堆上,其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变量。

变量的复制

  1. 对于block外的变量引用,block是将其复制到block的数据结构中来访问的;
  2. 对于__block修饰的外部变量,block是复制其引用地址来访问的;
  3. 对于数组,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有三种:

  1. NSConcreteStackBlock:只访问局部变量,成员变量,没有强指针引用。在函数返回后即销毁;
  2. NSConcreteMallocBlock:有强指针引用或者copy修饰,block被复制到堆中,没有强指针引用即销毁;
  3. NSConcreteGlobalBlock:没有访问外界变量,或只访问全局变量、静态变量,应用程序结束才销毁。

ARC中的Block

ARC中,将只有NSConcerteGlobalBlock和NSConcreteMallocBlock;

NSConcreteStackBlock类型会被NSConcreteMallocBlock替代;

由于ARC已经能很好的处理对象的生命周期的管理,这样所有对象都放到堆上管理,对编译器来说,实现比较方便。

最后,参考链接:http://blog.devtang.com/2013/07/28/a-look-inside-blocks/

感谢该博主的文章。