Block

又见block(一):block是什么? block的原理实质问题是一道分水岭,区分中高级程序的试金石,一般的大公司,他可能不会问你内存管理甚至多线程编程与简单的一笔带过,但是block一定会仔细问。无奈的是平时只知使用,对其原理与实质没有深究,因此数次面试倒在这道坎上,真是心有不甘,这次好好理理关于block的前因后果,以供后续随手翻阅查证。... 阅读详情

01基本认识

block的原理是怎样的?本质是什么?
block本质上也是一个OC对象,它内部也有个isa指针
block是封装了函数调用以及函数调用环境的OC对象
封装了函数调用以及调用环境的OC对象

__block的作用是什么?有什么使用注意点?

block的属性修饰词为什么是copy?使用block有哪些使用注意?
block一旦没有进行copy操作,就不会在堆上
使用注意:循环引用问题

block在修改NSMutableArray,需不需要添加__block?

^{
        NSLog(@"this is a block");

    }();//调用的话在后面加上小括号就可以了

block本质上也是一个OC对象,它内部也有个isa指针

block是封装了函数调用以及函数调用环境的OC对象

block的底层结构如右图所示

block底层结构

我们可以把代码转成cpp文件的代码查看一下block的组成,他第一个变量就是isa指针,说明他本质是一个对象,他也会把外面访问变量在内部也创建一个,封装到block的结构体之中

cpp文件代码
struct __block_impl {
  void *isa;//
  int Flags;
  int Reserved;
  void *FuncPtr;//函数地址,block里面执行的那些代码放到这里
};
 struct __ViewController__viewDidLoad_block_desc_1 {
  size_t reserved;//保留的可能还有其他的用途
  size_t Block_size;//block占据多少内存
 };
struct __ViewController__viewDidLoad_block_impl_1 {
    struct __block_impl impl;
    struct __ViewController__viewDidLoad_block_desc_1* 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;
  }
};
//封装了block执行逻辑的函数
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_g1_c9nxm0n17mq959qfc_j0wtnr0000gn_T_main_2f788f_mi_0);

        }
- (void)viewDidLoad {
    [super viewDidLoad];
    ^{
        NSLog(@"this is a block");

    }();

    void (^block)(void) = ^{
        NSLog(@"this is a block");

    };
    struct __ViewController__viewDidLoad_block_impl_1 *block1 =(__bridge struct __ViewController__viewDidLoad_block_impl_1 *)block;
    block();
  //创建一个block。调用了一个结构体的构造函数。返还给我门一个结构体
        void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));//
        //执行block
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

我们可以通过断点去内存查看,这段block的执行的代码的开始地址,就是FuncPtr的存的地址

02底层数据结构

03变量捕获01-auto变量

假如需要参数,重新生成cpp代码

void (^block)(int , int ) = ^(int a, int b)
        {
            NSLog(@"%i", a+b);

        };
        block(10,23);


        ((void (*)(__block_impl *, int, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 10, 23);

//更复杂引用外部

int age = 10;
       void(^block)(void) = ^
        {
            NSLog(@"%i", age);//10
        
        };
/*
int age = 10;
       void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age));
*/
        age = 20;
        block();

//内部实现
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int age;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

引用外部的变量之后,我们发现block的内部结构发生了改变,多了一个age成员变量,这个变量的值是在编译的时候就传到了block的机构体内部,所以后面的更改不会影响他的值,因为block 执行函数里面的代码时候,使用的是自己的age,而不是外部的age.这就是为什么打印的是10

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int age = __cself->age; // bound by copy

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_g1_c9nxm0n17mq959qfc_j0wtnr0000gn_T_main_218f06_mi_0, age);

        }

04-变量捕获02-static变量

为了保证block内部能够正常访问外部的变量,block有个变量捕获机制,所谓的捕获就是block内部有个专门的成员来存储那个传进来的值,
默认情况下我们定义出来的变量,前面都有一个auto,所以平时我们都是省略,离开作用域就会销毁。捕获的时候是值传递,
static 局部变量

int age = 10;
        static int height = 10;
       void(^block)(void) = ^
        {
            NSLog(@"%i-%i", age,height);//10-20

        };
/*
 int age = 10;
        static int height = 10;
       void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age, &height));//他传进去的是地址
*/
        age = 20;
        height = 20;
        block();
/*
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int age = __cself->age; // bound by copy
  int *height = __cself->height; // bound by copy

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_g1_c9nxm0n17mq959qfc_j0wtnr0000gn_T_main_3bdf0b_mi_0, age,(*height));//访问的是指针变量所指向的值。

        }
*/
//内部代码实现
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int age = __cself->age; // bound by copy
  int *height = __cself->height; // bound by copy存的是外面变量的地址值

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_g1_c9nxm0n17mq959qfc_j0wtnr0000gn_T_main_3bdf0b_mi_0, age,(*height));

        }

为什么会有这样的差异,因为auto可能会销毁,age可能在内存中消失,我们以后访问的时候防止他消失我们访问不到数据,static就算是函数执行完了也会存在内存,不用担心他会销毁,所以可以传递地址

block捕获变量关系图

变量捕获03-全局变量

block使用外部的全局变量不会捕获到内部,而是直接使用

void(^block)(void) = ^
             {
                 NSLog(@"%i-%i", _age,height_);//20-20

             };
             _age = 20;
             height_ = 20;
             block();
//
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

                 NSLog((NSString *)&__NSConstantStringImpl__var_folders_g1_c9nxm0n17mq959qfc_j0wtnr0000gn_T_main_1ee27a_mi_0, _age,height_);

             }


//
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;
 }
};

因为他是一个全局变量,在哪里都可以访问他,所以不需要捕获,局部变量需要捕获是因为作用域的问题,可能出现跨函数访问变量,

-(void)test{
    void (^block)(void)=^{
        NSLog(@"%@", self);

    };
    block();
}
//
static void _I_Person_test(Person * self, SEL _cmd) {
    void (*block)(void)=((void (*)())&__Person__test_block_impl_0((void *)__Person__test_block_func_0, &__Person__test_block_desc_0_DATA, self, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

//struct __Person__test_block_impl_0 {
  struct __block_impl impl;
  struct __Person__test_block_desc_0* Desc;
  Person *self;
  __Person__test_block_impl_0(void *fp, struct __Person__test_block_desc_0 *desc, Person *_self, int flags=0) : self(_self) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

说明他会被捕获,说明self是一个局部变量。函数默认有两个参数self 和cmd.
_name可以堪称self->name;说明他也会被捕获,但是捕获的是self对象。而不是单独的对_name捕获。
用self.name,还是要捕获self

iOS 底层探索篇 ——block(下) @[TOC](iOS 底层探索篇 —— block(下)) block底层源码 block 编译成一个什么样的结构 block invoke-> isa->签名 ->捕获 -> 保存 -> 释放 1. block底层分析 要探究block底层是什么样的一个结构,那么就定义一个block,然后xcrun 一下。 xcrun之后,打开生成的cpp文件,看到生成的main函数。 把函数的类型强转去掉之后得到下面的代码。这里看到 void(*block)(void) 等于__ma 阅读详情

相关推荐

全球尺度遥感云计算平台:Google Earth Engine

Google Earth Engine(GEE)是一个基于 Google 云服务基础设施的遥感大数据分析平台,它结合 Google 强大的云计算能力与 NASA、ESA、NOAA 等机构的空间数据,用于在全球尺度解决一些非常重要的社会问题:森林退化、粮食安全、灾害预警、水资源管理、气候监测以及环境保护等。 GEE 官方宣传视频(版权归 Google 所有) Spacefan 的视频 ·303播放 发展背景 2008 年,手握 40 多年 LandSat 数据的美国地质调查局(USG...

qq_41240287的博客 5014

Blocks

Blocksblocks是C语言的扩充功能。blocks是带有自动变量(局部变量)的匿名函数。截获自动变量 int main() { int dmy = 256; int val = 10; const char *fmt = "val = %d\n"; void (^blk)(void) = ^{

haisheng1005874221的专栏 431

主板USB接口全部失效解决方案(通用串行总线USB控制器有黄色叹号)保姆级教程亲测有效

通用串行总线控制器总是有叹号解决办法 办公室的崩腾时代老电脑,疫情期间没人用,来了开机之后发现鼠标键盘都用不了,本以为是主机放时间长出了问题,后来想起来就百度了“鼠标键盘都失效”的标题,结果发现了“串行总线控制器总是感叹号”的关联问题。正好办公室电脑都装有teamviwer常用来远程办公(在家偷懒)。所以通过远程发现电脑室可以运行的。并在控制面板发现:通用串行总线控制器总是感叹号,插入外设时USB无反应。如图:​ 并且右键选中其中一个,点击最后一个选项:“属性” 可以发现“设备状态”中会提示“由...

xiao_bai_0_0的博客 2万+

Objective-C中Block详解

Block 是 Objective-C 语言的一种特性,它允许你定义一段可重用的代码,并在你的代码中像变量一样使用这段代码。Block 可以捕获和存储它所在的环境中的变量和常量。Block 是一种可执行的代码块,可以看作是带有自动捕获变量能力的匿名函数。在这个示例中,是一个 Block,它没有参数也没有返回值,当被调用时,它会打印一条消息。

Gamin 2335

Maven

是一款自动化构建工具,专注于Java项目的构建、依赖的管理。

weixin_58738870的博客 2163

ICC II 时钟树综合(CTS)前奏:从约束定义到NDR规则的全方位设置指南

本文详细介绍了ICC II中时钟树综合(CTS)的前期准备工作,包括约束定义、NDR规则设置和时钟平衡点控制等关键步骤。通过实际案例和代码示例,指导工程师如何优化时钟偏斜、插入延迟和信号质量,确保芯片设计的时序收敛和性能稳定。特别强调了非默认布线规则(NDR)在时钟网络设计中的重要性。

weixin_42530458的博客 212

Block 本质、实现原理、内存管理、循环引用、__block等

一、Block介绍 1.1概念: 将函数及其执行上下文封装起来的对象 底层用struct实现 1.2block实现原理: a .新建项目 代码放入file.m中 b.打开终端cd到项目目录下 c.敲clang -rewrite-objc file.m查看编译后的文件内容 本质是将OC重写为C后目录下多了个file.cpp文件 1)Block实际上是OC对于闭包(闭包是一个函数或指向函数的指针,再加上该函数执行的外部的上下文变量。)的实现。OC是对C语言的扩展,block的实现是基于函数和指向函数的

idealPC的专栏 2331

巨某量引擎后台登录实战笔记 | Playwright自动化框架

自动化框架、Playwright、滑块、巨某量引擎、抖某音系

什么都会一点。 2080

通过__block的作用深入研究block

block普通引用 默认情况下,在block中访问外部变量是通过复制一个变量来操作的,既可以读,但是写操作不对原变量生效,下面通过代码来举证 NSString *a = @"testa"; NSLog(@"block前,a在堆中的地址%p,a在栈中的地址%p",a,&a); void(^testBlock)(void) = ^(void){ NSLog(@...

weixin_34220179的博客 583

RTK_ROS_导航(4):接入move_base

现在我们要将之前的数据接入到Move_base中,使得机器人能够根据Map上的终点进行导航,并且实现避障效果如下图。

旋风小短腿 1947

【100%通过率】华为od机试C卷【特殊的加密算法】Python 实现

有一种特殊的加密算法,明文为一段数字串,经过密码本查找转换,生成另一段密文数字串。规则如下1.明文为一段数字串由0-9组成2.密码本为数字0-9组成的二维数组3.需要按明文串的数字顺序在密码本里找到同样的数字串,密码本里的数字串是由相邻的单元格数字组成,上下和左右是相邻的,注意:对角线不相邻,同一个单元格的数字不能重复使用。

MISAYAONE的博客 5924

IOS7学习----------_block

没有__block qualifier的primitive c types会直接在创建block的时候被capture到block里。 有__block qualifier的话,会在调用block的时候取&number地址所指向的值,如果调用block的时候已经超过了number声明的scope,那么结果是undefined。

moyuxueyi的专栏 569

基于PointNet++网络的点云数据分类仿真分析

局部特征学习模块用于学习点云中每个点的局部特征表示,而全局特征学习模块用于学习整个点云的全局特征表示。点云数据分类是指将点云数据分为不同的类别或标签。PointNet++是一种用于点云数据处理的深度学习网络,它能够有效地学习点云数据的特征表示并实现高精度的分类。在本文中,我们将使用MATLAB实现基于PointNet++网络的点云数据分类仿真分析,并提供相应的源代码。然后,我们使用splitEachLabel函数将数据划分为训练集和测试集,其中80%的数据用于训练,20%的数据用于测试。

HackDashX的博客 210

block 归纳总结 上

block有三种类型:堆block,栈block,全局block。 对于这三种类型的block遵循俩个原则: block如果没有使⽤外部变量,或者只使⽤静态变量和全局变量,那⼀定是全局blcok。 block如果使⽤了外部变量,⽽且不是静态变量或全局变量,如果赋值给强引⽤的是堆block, 如果赋值给弱引⽤的是栈blcok。因为block在创建的时候,它的内存是分配在栈上的,⽽不是在堆上。栈区的特点是:对象随时有 可能被销毁,⼀旦被销毁,在调⽤的时候,就会造成系统的崩溃。所以我们要使⽤copy把它拷⻉ 到堆

lingjunjie的博客 1180

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

iOS底层探索之Block(五)——Block源码分析(__block 底层都做了什么?)

回顾 在上一篇博客中,通过对block追根溯源,汇编跟踪调式,源码分析,对底层结构和 block的属性方法都有一定的认识, 那么本篇博客将继续对block的底层进行分析。 iOS底层探索之Block(一)——初识Block(你知道几种Block呢?) iOS底层探索之Block(二)——如何解决Block循环引用问题? iOS底层探索之Block(三)——Block的本质 iOS底层探索之Block(四)——Block的探索和源码分析 1. block底层探索 block的结构和签名都分析完了,但是blo

俊而不逊的博客 722

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

iOS——Block签名

Block签名

cyberjack的博客 1787

BlockHook 兼容性实战:Stack Block、Global Block 与 dispatch_block_t 的 Hook 技巧合集

BlockHook 是一款基于 libffi 的 Objective-C Block 挂钩(Hook)工具,能在不改动业务代码的前提下,对任意 Block 进行 Before、Instead、After、Dead 四种模式的拦截与增强。很多初学者会以为 Block 只有一种形态,但在实际项目中,Stack Block(栈 Block)、Global Block(全局 Block)和 `dispat

gitblog_00571的博客 1030

GCD 中的 Block

GCD 中的 Block 在 GCD 中,可以使用 block.h 中的函数实现 block 的创建、执行和取消。 dispatch_block_t dispatch_block_create(dispatch_block_flags_t flags, dispatch_block_t block); 该方法会根据提供的 block 以及 flags 标识,在堆上创建一个新...

集腋成裘 620

block对copy的实现

对block进行copy经常会用到,block的copy底层是什么实现的?之前的文章介绍了block的数据结构和内存管理,本篇说一下block的copy实现。 copy的范围 case 1 // struct __main_block_impl_0 需要copy 示例代码 int age = 6; void(^block)(void) = ^{ NSLog(@"a==%d",age); }; case 2 // struct __main_block_impl_0 需要copy NSObje

iOS随笔 1071

Block使用方法总结

- block 本质 block 本质上也是一个OC对象,它内部也有一个 isa 指针 block是封装了函数调用以及函数调用环境的OC对象 - block 内调用局部变量和全局变量的原理: 1、block内调用局部变量时:会把局部变量捕获到block内,也就是block内会创建一个对象来接收局部变量,比如局部变量是int a;那么block内就会也创建一个int a;来接收外面的局部变量,这时接...

蓝云阁主的博客 1846

【iOS】Block总结

之前的Block写的没啥重点,这一次总结一下重点,比如Block如何捕获外界变量,__block的使用Block的重点其实就在于捕获自动变量与使用__block修饰时forwarding指针的变化,还有其发生拷贝的时机:使用__block变量,使用dispatch API, 手动copy与作为函数返回值另外重要的还有block的循环引用,后面会一起总结循环引用。

weixin_72437555的博客 1502

Block小结

一直使用Block,却没有认真研究过,做个总结吧 block的实质就是匿名函数,通过函数指针的调用来实现的,并对内部的引用到的数据进行管理(retain/release),封装后成为block,最终变成对象。 1.Block结构 void (^block)(void) = ^(void) { printf("hello world"); }; 通过clang -rew...

勿在浮沙筑高楼 354

iOS中的block block是什么,block的本质

iOS中的block block是什么,block的本质 block 是c语言的扩充功能,是带有自动变量(局部变量)的匿名函数,也称之为 闭包。 先说结论,block中包含了一个isa指针,所以它是一个对象,一个包含了函数调用和函数调用环境的OC对象(那么是swift对象吗?感兴趣的,可以自己动手研究下)。 1.转换代码,分析结构 以下是过程:①写个简单的block ② 使用命令clang -rewrite-objc hello.m 转为c++ 互相对比可以看出,我们对block的...

iOS圈圈的博客 743

【iOS】 Block再学习

笔者之前学习过block的相关内容,但是掌握不牢,今天再重新学习一遍。

qq_73106050的博客 1604

Block 签名信息的使用

Block在OC中却是一个非常神奇的存在,即具备了对象的特性(具备isa指针),又具备了方法的特性(具有自己的实现函数),这就自然而然注定了Block会与众不同.除了常规使用之外,还可以利用Block做一些"骚操作". Block签名 在OC中中所有的方法都有编码信息,签名信息包含了方法的参数类型和返回值类型等信息.对于对象的实例方法可以利用 Method method =...

王二东的博客 1025

block才会执行 mono_浅析Block闭包

浅析Block闭包 简单来说,block就是将函数及其上下文封装起来的对象,从功能上可以把它看作是C++中的匿名函数,也可称之为块。Block类型写法:返回值+(^块名)+(参数)= ^(参数){ 内容 }如下所示:int (^myBlock)(int a, int b) = ^(int a, int b){ return a + b; };Block结构Block存储区域Block本质...

weixin_28752415的博客 517
上一篇: Category-关联对象
下一篇: KVC
I微微一笑I
博客等级 码龄11年 0粉丝 86原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值