前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >深入理解Block

深入理解Block

作者头像
Jacklin
发布于 2019-11-06 08:13:06
发布于 2019-11-06 08:13:06
99200
代码可运行
举报
文章被收录于专栏:攻城狮的动态攻城狮的动态
运行总次数:0
代码可运行

本文约5000字,大概阅读时间20分钟。

我用了将近一周的时间将与Block相关的知识点进行了整理,将相关内容整理成了脑图,分享给大家:

说起Block,我想大家再熟悉不过了。每天的开发过程中都在与他打着交道,简洁高效的使用给我们的工作带来了便利。

正是因为Block对于我们而言不可或缺,我更觉得应该抽出时间好好认识一下Block,了解一些他的优缺点、过往经历,让我们对它的理解更深一层,进而开发出更加高效的程序。


Block与C函数的区别和联系

Block是C语言的扩充,常被称为带有局部变量的匿名函数; 看一下正规的C语言函数定义:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int test(int count);
int result = test(10);

以上声明了函数名为test函数; 使用函数指针调用函数,如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int (*testFuncPtr)(int) = &test;
int result = (* testFuncPtr)(10);

对比Block,Block就可以定义为不带名称的函数,说的直白一点,就是匿名函数,即不用显式的定义函数名称,但是匿名函数都有固定的一个表达式。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
^(int count){
  printf("%d", count);
}

等价于:
void test(int count){
}

Block语法定义格式如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
^ 返回值类型 参数列表 表达式

省略之后:

^ 表达式

通过上面的例子可以看出两点不同:

  • 没有函数名称;
  • 带有“^”符号;

Block的基本使用

可以通过定义Block类型的变量对Block进行调用,与函数指针有相似之处,具体如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int (^block)(int) = ^(int count){
      return count + 1;
}
block(5);

Block类型变量用途:局部变量、参数、静态变量、全局变量以及静态全局变量等等。

Block 可以作为参数进行传递,也可以作为返回值进行传递,具体如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
- (void)testFuncWithBlock:(void (^)(int count))block{
    if (block) {
        block(5);
    }
}

// 将Block作为返回值,代码摘自·Masonry·
- (MASConstraint * (^)(id))equalTo {
    return ^id(id attribute) {
        return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
    };
}

具体以Masonry使用为例讲解一下,Block调用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Masonry 在常用布局时用到方法如下:
make.top.equalTo(self.titleLabel.mas_bottom).offset(15);

// 有些同学可能对上述的链式调用不是很清楚,其实上述链式调用就是使用Block实现的,分解如下:
MASConstraint *constraint = make.top;
MASConstraint *(^equalBlk)(id) = constraint.equalTo;
constraint = equalBlk(self.titleLabel.mas_bottom);
MASConstraint *(^offsetBlk)(CGFloat) = constraint.offset;
constraint = offsetBlk(15);

可使用关键字typedef,进行Block类型变量的定义

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
typedef int(^Blk)(int);

// 调用
Blk blk = ^int (int count){
        return count ++;
    };
int result = blk(5);

Block 作为属性变量的用法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@property (nonatomic, copy) void (^successCompletion)(int);
@property (nonatomic, copy) Blk failCompletion;

通过上面一小节了解到:

  • 函数与Block区别与联系;
  • Block变量和Block表达式的联系

Block的实质

在前面小节中,介绍了Block的基本用法。那么问题来了,Block究竟是什么呢?现在公认的说法是带有自动变量的匿名函数,动手一探究竟。

使用Clang -rewrite-objc命令对下面代码进行编译:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void testBlock(){
    int (^blk)(int) = ^(int count){
        return count ++;
    };
    blk(5);
}

执行完命令会在同级目录下生成XX.cpp文件,这个文件就是编译好的文件。 由于编译后文件包含很多C函数声明,在此就不一一解释了,主要关注上述Block方法实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

static struct __testBlock_block_desc_0 {
    size_t reserved;
    size_t Block_size;
} __testBlock_block_desc_0_DATA = { 0, sizeof(struct __testBlock_block_impl_0)};

struct __testBlock_block_impl_0 {
    struct __block_impl impl;
    struct __testBlock_block_desc_0* Desc;
    
    __testBlock_block_impl_0(void *fp, struct __testBlock_block_desc_0 *desc, int flags=0) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;�
        Desc = desc;
    }
};

static int __testBlock_block_func_0(struct __testBlock_block_impl_0 *__cself, int count) {
    
    return count ++;
}

函数具体的实现如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void testBlock(){
    
    int (*blk)(int) = ((int (*)(int))&__testBlock_block_impl_0((void *)__testBlock_block_func_0, &__testBlock_block_desc_0_DATA));
    ((int (*)(__block_impl *, int))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, 5);
}

虽然编译后Block方法很长,但是并不复杂,Block调用就像普通的C函数的使用。

首先,看一下源代码中Block方法体实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int (^blk)(int) = ^(int count){
        return count ++;
    };
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static int __testBlock_block_func_0(struct __testBlock_block_impl_0 *__cself, int count) {
    
    return count ++;
}

可以发现两者在语法格式上非常相近,实际上Block所使用的匿名函数就是被作为简单的C函数处理的。

在代码中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
__testBlock_block_impl_0 *__cself

其中,参数__cself相当于C++中指向实例自身变量的this或者Objective-C中的self。参数__cself在这里是__testBlock_block_impl_0结构体的指针。

然后,看一下__testBlock_block_impl_0结构体的组成:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct __testBlock_block_impl_0 {
         struct __block_impl impl;
         struct __testBlock_block_desc_0* Desc;
};

去掉代码中加入的构造函数,感觉结构上清晰多了。第一个成员变量是impl,__block_impl结构体的声明如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

其中,有关对象isa指针的理解可以参考之前写过的文章Objective-C Runtime:深入理解类与对象

第二个成员变量Desc,主要保存block所在内存的区域以及Block的大小。

关于__testBlock_block_impl_0结构体构造函数如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
__testBlock_block_impl_0(void *fp, struct __testBlock_block_desc_0 *desc, int flags=0) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;�
        Desc = desc;
    }

isa指针指向_NSConcreteStackBlock, __testBlock_block_impl_0结构体相当于objc_object结构体的Objective-C类对象的结构体。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
isa = &_NSConcreteStackBlock;

上述代码表明_NSConcreteStackBlock相当于objc_class结构体的实例。从而得知,Block就是Objective-C的对象了。 具体关于Objective-C类与对象的知识可以参考另一篇文章深入理解类与对象。

对于上述构造函数调用部分如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int (*blk)(int) = ((int (*)(int))&__testBlock_block_impl_0((void *)__testBlock_block_func_0, &__testBlock_block_desc_0_DATA));
    ((int (*)(__block_impl *, int))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, 5);

看着有些繁琐,对其去掉相关转换的部分:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct __testBlock_block_impl_0 temp = __testBlock_block_impl_0(__testBlock_block_func_0, &__testBlock_block_desc_0_DATA);
struct __testBlock_block_impl_0 *blk = & temp;
(*blk->impl.FuncPtr)(blk, 5);

上述代码中,将__testBlock_block_impl_0结构体实例的指针赋值给变量blk,从而知道源代码中的Block表达式就是__testBlock_block_impl_0结构体类型的变量,同时也是该结构体在栈上生成的实例。

从函数指针调用得知,由Block语法转换的__testBlock_block_func_0函数指针被赋值到__testBlock_block_impl_0的成员变量FuncPtr,同时说明了__testBlock_block_func_0中的__cself指向Block值。


截获局部变量

在开发过程中,我们经常遇到在Block中截获局部变量或者改变局部变量,如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
- (void)testBlock{
    
    int value = 1;
    void (^block) (void) = ^{
        NSLog(@"value = %d", value);
    };
    block();
}

看到这里,可能就有疑问了,Block内部是如何获取局部变量的,带着疑问重新Clang了一下源文件,似乎找到了答案,摘取了关键代码如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static void _I_TestBlock_testBlock(TestBlock * self, SEL _cmd) {

    int value = 1;
    void (*block) (void) = ((void (*)())&__TestBlock__testBlock_block_impl_0((void *)__TestBlock__testBlock_block_func_0, &__TestBlock__testBlock_block_desc_0_DATA, value));
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);

}

//Block内部实现:
static void __TestBlock__testBlock_block_func_0(struct __TestBlock__testBlock_block_impl_0 *__cself) {
  int value = __cself->value; // bound by copy
  NSLog((NSString *)&__NSConstantStringImpl__var_folders_pf_nyb_8kl12yq6v2sxzwdr5v8w0000gn_T_TestBlock_516a43_mi_0, value);
}

// Block结构体
struct __TestBlock__testBlock_block_impl_0 {
  struct __block_impl impl;
  struct __TestBlock__testBlock_block_desc_0* Desc;
 // 将局部变量追加成成员变量 
 int value;

  __TestBlock__testBlock_block_impl_0(void *fp, struct __TestBlock__testBlock_block_desc_0 *desc, int _value, int flags=0) : value(_value) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

通过上述Block语法表达式中使用的局部变量作为成员变量被加到了__TestBlock__testBlock_block_impl_0结构体中,同时,加到结构体中的成员变量的类型与局部变量的类型保持一致。当然了,如果Block表达式中没有使用局部变量,则不会加入到Block的结构体中。

通过初始化Block实例的构造方法和调用也可以看到局部变量value作为参数传递,具体如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//Block 初始化方法
  __testBlock_block_impl_0(void *fp, struct __testBlock_block_desc_0 *desc, int _value, int flags=0) : value(_value)

// 初始化方法的调用:
void (*block) (void) = ((void (*)())&__testBlock_block_impl_0((void *)__testBlock_block_func_0, &__testBlock_block_desc_0_DATA, value));

到这里,大致明白了Block如何截获局部变量value的,小结如下:

  • 1、系统编译时,根据Block是否需要截获外部变量来判断是都将局部变量作为成员变量保存到Block的结构体__testBlock_block_impl_0中;
  • 2、若需要截获局部变量,需要在初始化结构体函数中添加与局部变量相同类型的参数;
  • 3、局部变量就通过初始化构造函数传递到Block结构体__testBlock_block_impl_0,同时赋值给Block中的成员变量;
  • 4、在执行Block时,局部变量value会初始化__testBlock_block_impl_0结构体实例, 如下所示;
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  impl.isa = &_NSConcreteStackBlock;
    impl.Flags = 0;
    impl.FuncPtr = __testBlock_block_func_0;
    Desc = &__testBlock_block_desc_0_DATA;
    value = 1;

Block中使用C语言数组

只使用C语言的字符串数组,也没有向捕获的局部变量赋值,然而下面的代码再编译时就会产生错误,如下所示

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int testBlock1(){
    
    const char country[] = "China";
    void (^block) (void) = ^{
        printf("text=%c",  country[1]);
    };
    block();
    
    return 0;
}

编译的时候会出现错误:

看到这,你会感到疑惑,上面刚讲到Block是如何截获局部变量的,为什么到这里就失效了呢?

这是因为,Block中,没有实现对C语言数组的截获。通过上述截获局部变量的理论,C语言数组会作为成员变量保存到Block实例的结构体中,在初始化Block时,由成员变量赋值给局部变量,猜测代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void func(char a[]){
    char b[] = a;
}

从而看出端倪,将C语言数组类型变量赋值给C语言数据类型变量,这个在C语言下是无法编译,C语言不允许这样编译的。

如何解决呢? 目前,可以改成C语言数组的形式就可以解决上述问题了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int testBlock1(){
    
    const char *country = "China";
    void (^block) (void) = ^{
        printf("text=%c", country[1]);
    };
    block();
    
    return 0;
}

__block关键字的作用

前面的小节中,我们仅仅讲解了在Block中截获局部变量,那么,在Block中修改截获的局部变量又会带来什么样的问题呢?

通过上面的代码可以看出,虽然在Block中不能对局部变量进行修改,但是在全局变量、静态全局变量、静态变量都是可以在Block中进行修改的,具体如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int globalValue = 10;
static int staticGlobalValue = 5;
- (void)testBlockFunc {
    static int value = 5;
    void (^block)(void) = ^{
        
        globalValue = 11;
        staticGlobalValue = 12;
        value = 3;
        printf("globalValue = %d \n staticGlobalValue = %d \n value = %d \n", globalValue, staticGlobalValue, value);
    };
    
    block();
}

打印的结果如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
2018-12-02 21:56:47.323727+0800 TestBlock[38880:1182883] 
globalValue = 11 
staticGlobalValue = 12 
value = 3

再次使用clang编译了一下,发现静态全局变量和全局变量转换前后没有任何变换,大家可能疑问静态局部变量是如何转换的呢?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
__TestBlockMemory__testBlockFunc_block_impl_0 *__cself) {
  
    int *value = __cself->value; // bound by copy
        globalValue = 11;
        staticGlobalValue = 12;
        (*value) = 3;
        printf("globalValue = %d \n staticGlobalValue = %d \n value = %d \n", globalValue, staticGlobalValue, (*value));
    }

与截获局部变量的转换很相似,唯一的区别是Int类型的变量转换成了指针,从而正确的改变了原有的值。

除了上述的几种方式可以在Block中修改变量值外,OC专门提供了一个说明符——__block。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int testBlock(){
    __block int number = 1;
    void (^block) (void) = ^{
        number = 2;
    };
    block();
    return 0;
}

将上述代码进行Clang编译,还原成以下代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct __Block_byref_number_0 {
  void *__isa;
__Block_byref_number_0 *__forwarding;
 int __flags;
 int __size;
 int number;
};

struct __testBlock_block_impl_0 {
  struct __block_impl impl;
  struct __testBlock_block_desc_0* Desc;
  __Block_byref_number_0 *number; // by ref
    
  __testBlock_block_impl_0(void *fp, struct __testBlock_block_desc_0 *desc, __Block_byref_number_0 *_number, int flags=0) : number(_number->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __testBlock_block_func_0(struct __testBlock_block_impl_0 *__cself) {
  __Block_byref_number_0 *number = __cself->number; // bound by ref

        (number->__forwarding->number) = 2;
}

惊奇的发现,局部变量number变成了结构体__Block_byref_number_0,Block的__testBlock_block_impl_0的结构体实例持有__block变量的__Block_byref_number_0结构体实例的指针。

__testBlock_block_impl_0实例的成员变量__forwarding指向实例自身,并通过__forwarding变量访问成员变量number

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
__Block_byref_number_0 *number = __cself->number; // bound by ref
 (number->__forwarding->number) = 2;

值得注意一点是,__Block_byref_number_0结构体并没有在__testBlock_block_impl_0结构体中,这样主要是为了在多个Block中共用该__block变量。


Block的储存

Block分为三类: _NSConcreteStackBlock

种类

存储域

Block变量类型

复制后的存储域

_NSConcreteStackBlock

局部/自动变量

由栈复制到堆上

_NSConcreteGlobalBlock

程序的数据区域(.data区)

全局变量

保持在数据区

_NSConcreteMallocBlock

局部变量

增加引用计数

定义在全局的Block在变量作用域之外通过指针访问使用,定义在栈上的__block局部变量和Block在超出作用域时将被废弃。那Block超出作用域是如何存在的呢?

这里就用到Block的Copy操作,即将分配在栈上的Block 和__block变量复制到堆上,从而延长了Block和__block变量的生命周期。

那么,Block被复制到堆上的操作是何时进行的呢?

首先,比较明确的一点是:将Block作为返回值返回时,编译器将会自动将Block由栈上复制到堆上,其他情况下,需要手动执行Copy操作;在ARC有效的情况下,Block是否被复制到堆上,编译器会进行相关的判断;

举个栗子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
typedef int (^BlockCase)(int);

BlockCase getBlockCallBack(int value) {
    return ^(int count) {
        return count + value;
    };
}

在ARC有效的情况下,作为返回值的Block会被复制到堆上。当然了,系统中存在的一些API是无需手动复制的:Cocoa框架方法中带有usingBlock方法、GCD相关的API等等。


__block变量的存储域

当在Block中使用__block变量时,当在Block从栈复制到堆上同时,被使用过的__block变量也会从栈复制到堆上。如下图所示:

__block变量所分配的存储域

Block从栈复制到堆上后的影响

由栈复制到堆上并被Block持有

被Block持有


__fawarding指针存在的意义

前边介绍了Block和__block变量从栈复制到堆上的情景,但都与__fawarding指针脱不开关系。

  • 由栈复制到堆之前,__fawarding指针指向自身;
  • 复制之后,栈上的__fawarding指针指向复制到堆上的__block变量,堆上的__fawarding指针指向自身。
  • 从而,解释了无论__block变量配置在栈上还是配置在堆上时都能正确的访问到__block变量;

Block截获对象

通过了解上节的内容,很好理解以下内容:

  • __block修饰的变量从栈复制到堆上,赋值给该__block变量的对象也被从栈复制到堆上,当__block变量从堆上释放时,该对象才能得到释放;
  • 当使用__weak修饰的__block变量在赋值的时候,由于赋值对象的作用域问题而释放,从而导致__block变量不能强持有该对象。

Block循环引用

形成原因:

  • 对象与Block相互持有;
  • 形成强持有环; 解决方式:
  • 设置weak弱持有关系;
  • 手动置为nil,梳理好持有关系,破坏环状结构。

结束

关于Block就大致说到这吧,从Block的使用到Block的原理剖析,希望能让我们更客观的认识Block,写出更高质量的代码。

预祝各位老铁少写Bug!!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-08-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 猿视角 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
《Objective-C-高级编程》干货三部曲(二):Blocks篇
这一章讲解了Block相关的知识。因为作者将Objective-C的代码转成了C++的代码,所以第一次看的时候非常吃力,我自己也不记得看了多少遍了。
用户1740424
2018/07/23
1.1K0
《Objective-C-高级编程》干货三部曲(二):Blocks篇
iOS 开发:『Blocks』详尽总结 (二)底层原理
在第一篇中我们讲解了 Blocks 的基本使用,也知道了 Blocks 是 带有局部变量的匿名函数。但是 Block 的实质究竟是什么呢?类型?变量?还是什么黑科技?
程序员充电站
2019/04/01
7320
iOS 开发:『Blocks』详尽总结 (二)底层原理
Block那些事
__main_block_func_0这个是block的执行函数,参数是__main_block_impl_0类型。
Helloted
2022/06/07
4510
Block那些事
Block
通过clang -rewrite-objc WYTest.m命令编译该.m文件,发现该block被编译成这个形式:
conanma
2021/10/28
6010
3. __block  __weak  __strong   这都是做什么的
1.1 局部变量 局部自动变量,在Block中可被读取。Block定义时copy变量的值,在Block中作为常量使用,所以即使变量的值在Block外改变,也不影响他在Block中的值,Block此时对局部变量只是做了值传递的操作。 1.2 static 修饰的全局变量 因为全局变量或静态变量在内存中的地址是固定的,Block在读取该变量值的时候是直接从其所在内存地址读出,获取到的是最新值,而不是在定义时copy的常量。 1.3 对OC对象的截获 NSMutableArray *array = [NSMut
陈雨尘
2018/06/07
6230
《Objective-C高级编程》温故知新之"Blocks"
在计算机科学中,此概念也称为闭包(Closure)、lambda计算等。Swift中称作闭包
Dwyane
2018/09/26
6460
iOS 进阶之 Block 的本质及原理
相信稍微有点开发经验的开发者,应该都对 block 有一定的了解。刚开始使用 block 的时候,可能觉得很方便。知道怎么去用,但是知其然知其所以然的态度来分析一下 block 的原理和内部结构是什么。
网罗开发
2021/01/29
6890
iOS 进阶之 Block 的本质及原理
Block原理探究(下篇)-捕获变量分析及__block原理
主要内容: 1.分析Block捕获外部变量的过程 2.理解Block修改外部变量的限制 3.分析__block存储域类说明符的原理 4.理解__block变量的存储域 5.探究Block对对象的捕获过程 6.Block的循环引用问题
梧雨北辰
2019/10/08
1.7K0
Block原理探究(下篇)-捕获变量分析及__block原理
利用Clang探究__block的本质
上一篇文章利用Clang探究block的本质得出的结论是block的本质是一个结构体对象,该对象包含调用block时要执行的函数指针。当我们在Objective-C层面调用block的时候,底层就可以通过调用block对应的C++结构体对象的函数指针来实现同等操作。
VV木公子
2019/11/04
7.3K0
老司机出品——源码解析之从Block说开去
近来把《iOS与OS X多线程和内存管理》这本书又掏出来看了一遍,这本书前前后后加起来看了能有三四遍了,每次看都有新的理解。现在就把个人对Block的一些理解记录下来。
老司机Wicky
2018/08/22
4740
老司机出品——源码解析之从Block说开去
深入研究Block捕获外部变量和__block实现原理
Blocks是C语言的扩充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了这个新功能“Blocks”。从那开始,Block就出现在iOS和Mac系统各个API中,并被大家广泛使用。一句话来形容Blocks,带有自动变量(局部变量)的匿名函数。
一缕殇流化隐半边冰霜
2018/08/30
1.2K0
深入研究Block捕获外部变量和__block实现原理
iOS block探究(二): 深入理解你要知道的block都在这里
你要知道的block都在这里 转载请注明出处 https://cloud.tencent.com/developer/user/1605429 三种block类型 NSGlobalBlock 如果block不捕获外部变量,那么在ARC环境下就是创建一个全局block。全局block存储在全局内存中,不需要在每次调用的时候都在栈中创建,块所使用的整个内存区在编译期已经确定了,因此这种块是一种单例,不需要多次创建。 NSMallocBlock 如果block捕获外部变量,那么在ARC环境下就是创了一个堆区blo
WWWWDotPNG
2018/04/10
1K0
block内部实现
c. 我们先看一下__ main_block_impl_0结构体和 __ main_block_func_0 还有__main_block_desc_0_DATA都代表写什么
老沙
2019/09/27
4240
【IOS开发高级系列】Block专题
http://blog.devtang.com/blog/2013/07/28/a-look-inside-blocks/
江中散人_Jun
2023/10/16
3540
【IOS开发高级系列】Block专题
利用Clang探究block捕获外部变量的本质(一)
我们知道,Objective-C的block会捕获自动变量。在计算机编程领域,自动变量(Automatic Variable)指的是局部作用域变量,即局部变量。相对于全局变量。如下,在main函数中声明一个局部变量val = 1;block中打印val的值,然后在执行block前修改val = 2;, 但是block依旧输出1。这就是所谓的block会捕获自动变量。 本篇文章主要探究block捕获局部变量的底层原理。除去函数参数外,关于block和静态变量、全局变量、静态全局变量的关系将在后面的文章展开讨论。
VV木公子
2019/11/12
7.9K0
Block原理探究(上篇)-Block本质及存储域问题
主要内容: 1.理解Block的本质 2.理解Block的存储域分类 3.理解Block的Copy原理
梧雨北辰
2019/10/08
1K0
深入解构iOS的block闭包实现原理
在iOS4出来后,苹果公司在OC中推出了block机制(也许更早就有了)。并且在后续的版本中大量的推广和使用了这项技术,比如对视图动画API的改版,比如GCD技术等等。block技术并不是什么新技术,他的本质就是闭包功能在iOS上的实现而已。而闭包功能在其他很多语言中都有实现,比如JAVA中接口的匿名实现。用闭包可以解决那些执行逻辑和上下文环境解耦的场景,如果从设计模式的角度来考虑的话闭包就是一种策略模式(Strategy)的实现。
欧阳大哥2013
2018/08/22
7770
BLOCK介绍及常见问题
这段时间小编在整理开发代码问题时发现开发同学在使用block时经常出现一些BUG,其中还有一些隐藏的很深的问题,这里小编就为大家介绍一下block的原理,简单用法和常见问题。
用户5521279
2019/10/28
7110
BLOCK介绍及常见问题
iOS Block的本质(四)
typedef void (^Block)(void); struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; struct __main_block_desc_0 { size_t reserved; size_t Block_size; void (*copy)(void); void (*dispose)(void); }; struct __Block_byref_age_0 { void *__isa; struct __Block_byref_age_0 *__forwarding; int __flags; int __size; int age; }; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; struct __Block_byref_age_0 *age; // by ref }; int main(int argc, const char * argv[]) { @autoreleasepool { __block int age = 10; Block block = ^{ age = 20; NSLog(@"age is %d",age); }; block(); struct __main_block_impl_0 *blockImpl = (__bridge struct __main_block_impl_0 *)block; NSLog(@"%p",&age); } return 0; }
用户1941540
2019/02/15
7050
iOS Block的本质(四)
面试驱动技术 - Block看我就够了【干货】
Block 在 iOS 算比较常见常用且常考的了,现在面试中,要么没面试题,有面试题的,基本都会考到 block 的点。本文特别干!(但是初中级iOSer应该能有所收获~)
小蠢驴打代码
2019/03/15
8040
面试驱动技术 - Block看我就够了【干货】
相关推荐
《Objective-C-高级编程》干货三部曲(二):Blocks篇
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验