Objective-C Block篇(一) : Block的实质、存储域

本文内容:

  1. 什么是Blocks
  2. Block类型变量
  3. Block的实质(带有(截获)自动变量值的匿名函数)
  4. Block的存储域

什么是Blocks

Blocks是C语言的扩充功能,可以用一句话表示这个功能:带有自动变量(局部变量)的匿名函数

编译后:就是在文件中的一个函数

# 在C中可能使用的变量

  • 自动变量(局部变量)
  • 函数的参数
  • 静态变量(静态局部变量)
  • 静态全局变量
  • 全局变量
    其中,在后三种,在文件中的任何地方都能访问到。
    研究Blocks的重点就是:在Blocks中,怎么访问、存储、改变前两种数据

“带有自动变量值的匿名函数”这一概念并不仅指Blocks,还存在与其他编程语言,也被称为:
Block: C+ Blocks、Smalltalk、Ruby
闭包(Closure):swift
lambda计算(λ计算,lambda calculus等):LISP、Python、C++ 11
Anonymous function(匿名函数):JavaScript

Block类型变量

完整形式的Block类型变量定义语法 与 C语言函数定义 仅有两点不同:

  • 没有函数名:因为是匿名函数
  • 带有 ^ :返回值类型钱带有'^'(插入记号,caret)记号,因为OS X、iOS中大量使用Block,便于查找
    不完整形式的Block类型变量:可以省略返回值类型、参数列表

# C 函数指针 类型变量

int func (int count)
{
return count + 1;
}
int (*funcptr) (int) = &func;

# OC Block 类型变量

在Block语法下,可将Block语法赋值给 Block类型的变量。
在Blocks中的文档中,“Block”既指源代码中的`Block语法`,也指由Block语法`生成的值`
int (^blk) (int);  

比较:

  1. 相比C而言,仅仅是将* 改成了 ^
  2. 调用起来没有区别

Block的实质

# 代码反编译看Block

clang(LLVM 编译器)具有转换为我们可读源代码的功能,我们可以通过-rewrite-objc将含有Block语法的源代码变换为C++的源代码,本质上是C语言源代码

clang -rewrite-objc 源代码文件名

代码转换:

int main(){
    void(^blk)(void)=^{ printf("Block\n");};
    blk();
    return 0;
}

源代码通过clang可变换为以下形式:

//从其名称可以联想到某些标志、今后版本所需的区域以及函数指针
struct __block_impl {
    void *isa;
    int Flags;
    int Reserved;
    vold *Funcptr;  
}

// Block类型变量对应的结构体
struct __main_block_impl_0 {
    struct __block_impl imp1;   
    struct __main_block_desc_0* Desc;
    //默认构造函数,C++中的语法
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
        imp1.isa = &_NSConcreteStackBlock;
        imp1.Flags = flags  //不传默认=0,Reserved默认也是0
        imp1.FuncPtr = fp;
        Desc = desc:
    }
};

//该函数的参数__cself相当于C++实例方法中指向实例自身的变量this,或是Objective-C实例方法中指向对象自身的变量self
//即参数__cself为`指向Block值的变量`
static void __main_block_func_0(struct __main_block_impl_0* cself)
{
    printf("Block\n");
}

//今后版本升级所需的区域和Block的大小
static struct __main_block_desc_0{
    unsigned long reserved;
    unsigned long Block_size;
} __main_block_desc_0_DATA = {
    0,
    sizeof(struct __main_block_impl_0)   //block对应结构体的实例大小
};

int main()
{
    //创建 构造函数
      /*
        相当于(《OC 高级编程》中写的):
        struct __main_block_impl_0 tmp = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
        struct __main_block_impl_0 * blk = &tmp;
        相当于源码:void(^blk)(void) = ^{printf("Block\n");};
       
        @param  __main_block_func_0  函数
        @param  &__main_block_desc_0_DATA 结构体指针  "静态全局变量"
      */
    void (*blk)(void) = (void(*)(void)) &__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA);  

    //调用
      /*
        问题:blk明明是__main_block_impl_0结构体类型指针的,为什么变成__block_impl结构体类型指针了?
        答:因为__block_impl作为__main_block_impl_0的首成员,所以两个结构体的首地址都是相同的,所以完全可以强转,只不过使用__block_impl就访问不了__main_block_impl_0结构体中其他的几个成员变量,只能访问__block_impl自己的。
      */
    ( (void (*)(struct __block_impl *)) ((struct __block_impl *)blk)->FuncPtr )((struct __block_impl *)blk);

    return 0;
}

# &_NSConcreteStackBlock是什么

在讲&_NSConcreteStackBlockisa之前,先了解一下classid这两个关键字的定义:

struct objc_class {
    Class isa;
}
typedef struct objc_class * class;

struct objc_object{
    Class isa;
}
typedef struct objc_object * id;


isa:是一个Class 类型的指针. 
1. 每个实例对象有个isa的指针,他指向对象的类Class
2. Class里也有个isa的指针, 指向meteClass(元类)。
3. 元类(meteClass)也是类,它也是对象。元类也有isa指针,它的isa指针最终指向的是一个根元类(root meteClass). 
4. 根元类的isa指针指向本身,这样形成了一个封闭的内循环。

元类保存了类方法的列表。当类方法被调用时,先会从本身查找类方法的实现,如果没有,元类会向他父类查找该方法。

综上:

  1. 每个对象、类本质上都是结构体,都有isa指针
  2. 每个结构体都持有对象、类的属性、方法的名称、方法的实现(函数指针)、以及父类的指针
  3. &_NSConcreteStackBlock相当于Block isa指向的类,在将Block作为OC对象处理时,关于该类的信息放置于_NSConcreteStackBlock中.

总结:Block即为Objective-C的对象,C中的结构体,底层实现是C语言中的函数(下一篇Objective-C Block篇(二) : Block捕获自动变量机制中将通过讲解Block的捕获机制,详细证明Block就是函数)

Block的存储域

Block存在三种不同作用域的对象:

  • _NSConcreteStackBlock设置在栈上
  • _NSConcreteGlobalBlock设置在程序的数据区域(.data区)
  • _NSConcreteMallocBlock设置在由malloc函数分配的内存块(即堆)中

# _NSConcreteGlobalBlock

  1. 像声明全局变量一样声明Block变量时
  2. 捕获全局变量、静态自动变量、静态全局变量时(此时,如果同时满足_NSConcreteMallocBlock的条件,那_NSConcreteMallocBlock优先
  3. 当不捕获任何自动变量时

# _NSConcreteMallocBlock

  1. 调用Block的copy实例方法时
  2. Block作为函数返回值返回时
  3. 将Block赋值给附有__strong修饰符id类型的类或Block类型变量时
  4. 在方法名含有usingBlock的Cocoa框架方法或Grand Central Dispatch的API传递Block时

调用objc_retainBlock()方法,实际上也就是Block_copy函数

  • 对栈上的Block执行copy方法,会从栈复制到堆
  • 对堆上的Block执行copy方法,引用计数增加
  • 对全局的Block执行copy方法,什么也不会发生
    所以,不管Block配置在何处,用copy方法都不会引起任何问题,不确定存储域的时候可以直接copy

# _NSConcreteStackBlock

除了以上讲述的情况,其他创建的Block存储于都是在栈上

ARC 无效时,一般需要我们手动将Block从栈复制到堆,然后手动释放。

  • -retain
  • -copy/Block_copy(block)
  • -release/Block_release(block)

对于栈上的block调用retain是无效的,只有先copy到堆上,再copy才会有效果

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

推荐阅读更多精彩内容