您的位置:首页 > 移动开发 > IOS开发

iOS Block底层实现原理详解

2016-04-28 14:57 1281 查看
block原始代码为:

int main(int argc, const char * argv[]) {
@autoreleasepool {

int a = 10;

void (^block)() = ^{
printf("%d\n", a);
};

//再次给a 赋值

a = 20;

block();
}
return 0;
}
将上面block原始代码转换为C++代码: 通过 命令 :clang -rewrite-objc main.m

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("%d\n", 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 (*block)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));

a = 20;

((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
block原始代码为:

int main(int argc, const char * argv[]) {
@autoreleasepool {

__block int a = 10;

void (^block)() = ^{
printf("%d\n", a);
};

//再次给a 赋值

a = 20;

block();
}
return 0;
}
将上面block原始代码转换为C++代码: 通过 命令 :clang -rewrite-objc main.m

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("%d\n", (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 (*block)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));//这里的<span style="font-family: Arial, Helvetica, sans-serif;">570425344 最后赋值给 </span><span style="font-family: Arial, Helvetica, sans-serif;">flags 但是 flags 直接又被赋为 0,这个</span><span style="font-family: Arial, Helvetica, sans-serif;">570425344在此处没用。</span><span style="font-family: Arial, Helvetica, sans-serif;">
</span><span style="font-family: Arial, Helvetica, sans-serif;">
</span>

(a.__forwarding->a) = 20;

((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };


所谓结构体类型的自动变量,即栈上生成的该结构体的实例。

设置对象的存储域
_NSConcreteStackBlock
_NSConcreteGlobalBlock程序的代码段(.text区)
_NSConcreteMallocBlock
存储域

通过上面的描述我们知道Block和__block变量实质就是一个相应结构体的实例。我们在上述转换过的代码中可以发现 __main_block_impl_0 结构体构造函数中, isa指向的是 _NSConcreteStackBlock。Block还有另外两个与之相似的类:

_NSConcreteStackBlock 保存在栈中的block,出栈时会被销毁

_NSConcreteGlobalBlock 全局的静态block,不会访问任何外部变量

_NSConcreteMallocBlock 保存在堆中的block,当引用计数为0时会被销毁

上述示例代码中,Block是被设为_NSConcreteStackBlock,在栈上生成。当我们把Block作为全局变量使用时,对应生成的Block将被设为_NSConcreteGlobalBlock,如:

该代码转换后的代码中,Block结构体的成员变量isa的初始化如下:

那么_NSConcreteMallocBlock在什么时候被使用呢?

分配在全局变量上的Block,在变量作用域外也可以通过指针安全的访问。但分配在栈上的Block,如果它所属的变量作用域结束,该Block就被废弃。同样地,__block变量也分配在栈上,当超过该变量的作用域时,该__block变量也会被废弃。

这个时候_NSConcreteMallocBlock就登场了,Blocks提供了将Block和__block变量从栈上复制到堆上的方法来解决这个问题。将分配到栈上的Block复制到堆上,这样但栈上的Block超过它原本作用域时,堆上的Block还可以继续存在。

复制到堆上的Block,它的结构体成员变量isa将变为:

而_block变量中结构体成员__forwarding就在此时保证了从栈上复制到堆上能够正确访问__block变量。在这种情况下,只要栈上的_block变量的成员变量__forwarding指向堆上的实例,我们就能够正确访问。

我们一般可以使用copy方法手动将 Block 或者 __block变量从栈复制到堆上。比如我们把Block做为类的属性访问时,我们一般把该属性设为copy。有些情况下我们可以不用手动复制,比如Cocoa框架中使用含有usingBlock方法名的方法时,或者GCD的API中传递Block时。

当一个Block被复制到堆上时,与之相关的__block变量也会被复制到堆上,此时堆上的Block持有相应堆上的__block变量。当堆上的__block变量没有持有者时,它才会被废弃。(这里的思考方式和objc引用计数内存管理完全相同。)
//
//  main.m
//  test_block_05
//
//  Created by admin on 4/28/16.
//  Copyright © 2016 jeffasd. All rights reserved.
//

#import <Foundation/Foundation.h>

void (^globalBlock)() =  ^(){
NSLog(@"fsdfsdf");
return;
};

int main(int argc, const char * argv[]) {
@autoreleasepool {

int foo = 3;
NSLog(@"the foo address is %p", &foo);

int foo1 = 4;
NSLog(@"the foo1 address is %p", &foo1);

int foo2 = 5;
NSLog(@"the foo2 address is %p", &foo2);

__block int a = 10;

//        int a = 10;

NSLog(@"11 %p", &a); //0x7fff5fbff7f8 地址很大在栈中

void (^block)() = ^{
NSLog(@"block a address %p", &a); //0x7fff5fbff7f8 地址很小在堆中
printf("%d\n", a);
};

//再次给a 赋值

NSLog(@"11 block is %@", block);

NSLog(@"%p", &a);

a = 20;

block();

NSLog(@"block is %@", block);

NSLog(@"the global block is %@", globalBlock);
globalBlock();
NSLog(@"the global block is %@", globalBlock);

__weak void (^weakBlock)() =  ^(){
NSLog(@"fsdfsdf");
return;
};

NSLog(@"the weak block is %@", weakBlock);

}
return 0;
}

//int main(int argc, const char * argv[]) {
//    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
//
//        int a = 10;
//
//        void (*block)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));
//
//
//
//        a = 20;
//
//        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
//    }
//    return 0;
//}
//static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

//切换成C++语音
//int main(int argc, const char * argv[]) {
//    @autoreleasepool {
//
//        __block int a = 10;
//
//        void (^block)() = ^{
//            printf("%d\n", a);
//        };
//
//        //再次给a 赋值
//
//        a = 20;
//
//        block();
//    }
//    return 0;
//}
////切换成C++语音
//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 (*block)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));
//
//
//
//        (a.__forwarding->a) = 20;
//
//        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
//    }
//    return 0;
//}
//static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };


block属性
这里还有一点关于block类型的ARC属性。上文也说明了,ARC会自动帮
strong类型
捕获外部变量
的block进行copy,所以在定义block类型的属性时也可以使用strong,不一定使用copy。也就是以下代码:

/** 假如有栈block赋给以下两个属性 **/

// 这里因为ARC,当栈block中会捕获外部变量时,这个block会被copy进堆中
// 如果没有捕获外部变量,这个block会变为全局类型
// 不管怎么样,它都脱离了栈生命周期的约束

@property (strong, nonatomic) Block *strongBlock;

// 这里都会被copy进堆中
@property (copy, nonatomic) Block *copyBlock;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: