OC中block捕获对象类型的变量

block捕获对象类型的变量

前面讲解的block变量捕获,我们讲解了block捕获基本数据类型的情况,下面我们再来分析下捕获对象类型的auto变量,分析下block的底层内存布局情况,我们创建一个新工程,创建一个Person类,代码如下:

Person类:

@interface Person : NSObject

@property (nonatomic, assign) int age;
@end


@implementation Person

- (void)dealloc {
    NSLog(@"%s",__func__);
}
@end

main函数:

typedef void(^MyBlock)(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        
        MyBlock block;
        
        {
            // person默认是__strong修饰符修饰的
            __strong Person *person = [[Person alloc] init];
            person.age = 10;
            
//            __weak typeof(Person) *weakP = person;
            
            // block变量强引用着^{},编译器就会进行`copy`操作,将这个block从栈拷贝到堆上
            block = ^{
                NSLog(@"-%d", person.age);
            };
        }
       
        // 执行block,上面的person对象已经出了函数作用域,但是还没有释放
        block();
    }
    return 0;
}

我们执行命令xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.mmain.m文件转换为c++文件,转换后代码如下:

main:函数

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
        
        MyBlock block;
        {
            Person *person = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));
            
            ((void (*)(id, SEL, int))(void *)objc_msgSend)((id)person, sel_registerName("setAge:"), 10);

            block = &__main_block_impl_0(
                                         __main_block_func_0,
                                         &__main_block_desc_0_DATA,
                                         person,
                                         570425344
                                         );
        }
        
        // 执行block
        block->FuncPtr(block);
    }
    return 0;
}

block结构体:

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
    
  // 在block结构体内部,person对象是强指针引用还是弱指针引用,取决于block内部访问的auto变量是__strong修饰还是__weak修饰的
  Person *__strong person;
    
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__strong _person, int flags=0) : person(_person) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

__main_block_desc_0:结构体:

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
    
    // 只有在block内部访问了auto对象类型的变量时,才会有`copy`和`dispose`函数指针存在,因为只有访问的是对象类型才需要进行内存管理相关操作
    
    // copy函数指针,指向__main_block_copy_0函数
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  
    // dispose函数指针,指向__main_block_dispose_0函数
  void (*dispose)(struct __main_block_impl_0*);
}

__main_block_copy_0函数:

// 当block调用copy操作,将block从栈拷贝到堆上时,就会调用此函数
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
    /**
    _Block_object_assign:负责内存管理相关操作,也就是说这个函数会根据block结构体内部捕获的变量的引用类型进行内存管理:
     1.如果block结构体内捕获的变量是__strong类型的指针,那么这个指针就对block外面的对象进行强引用,进行retain操作,引用计数器+1
     2.如果block结构体内捕获的变量是__weak类型的指针,那么这个指针就对block外面的对象进行弱引用
     */
    _Block_object_assign(
                         (void*)&dst->person,
                         (void*)src->person,
                         3/*BLOCK_FIELD_IS_OBJECT*/
                         );
}

__main_block_dispose_0函数:

// 当堆中的block需要销毁时,会调用此函数
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
    // block结构体内的强指针断开对外部auto变量的强引用时,进行release操作,引用计数器-1
    _Block_object_dispose(
                          (void*)src->person,
                          3/*BLOCK_FIELD_IS_OBJECT*/
                          );
}

block捕获对象类型auto变量底层结构分析总结如图:

image

讲解示例代码Demo地址:https://github.com/guangqiang-liu/06.4-BlockDemo

更多文章

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270