OC对象 - Block修改变量

【OC】block的实现 Block语法实际上是作为极普通的C语言代码来处理的。通过支持Block的编译器,含有Block语法的源代码转换为一般C语言编译器能够处理的源代码,并作为极为普通的C语言源代码被编译。这只不过是概念的问题,在实际编译时无法转换成我们能够理解的源码,但clang具有转换成我们可读源代码的功能。通过“-rewrite-objc”选项就能将含有block语法的源代码转换为C++的源代码。说是C++,其实尽是使用了struct结构,其本质是C语言的源代码。clang -rewrite-objc 源代码文件名。 阅读详情

OC对象 - Block修改变量

1. 尝试修改

如下代码:

typedef void (^ZSXBlock)(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        int age = 10;
        
        ZSXBlock block = ^ {
            age = 20;
            NSLog(@"age is %d", age);
        };
        
        block();
    }
    return 0;
}

尝试修改age的值,但是编译报错了

1.1 底层实现

将上述代码转成C++

此时会报错,先把age = 20注释掉

直接修改肯定是改不了的

2. 可行修改方法

2.1 static

typedef void (^ZSXBlock)(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        static int age = 10;
        
        ZSXBlock block = ^ {
            age = 20;
            NSLog(@"age is %d", age);
        };
        
        block();
    }
    return 0;
}


给age增加static修饰符后,可以修改

2.1.1 底层实现


通过指针的方式访问,自然是可以修改了

2.2 全局变量

全局变量,全局都可以直接访问,不经过结构体指针,就可以直接修改的

typedef void (^ZSXBlock)(void);

int no_ = 10;

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        static int age = 10;
        
        ZSXBlock block = ^ {
            age = 20;
            no_ = 30;
            NSLog(@"age is %d", age);
        };
        
        block();
    }
    return 0;
}

2.3 __block

更多时候,我们希望可以修改局部变量,如果还使用static、全局变量的方式,那变量就一直在内存中了,我们肯定不希望这样。这时候就可以使用__block修饰符

typedef void (^ZSXBlock)(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        __block int age = 10;
        
        ZSXBlock block = ^ {
            age = 20;
            NSLog(@"age is %d", age);
        };
        
        block();
    }
    return 0;
}


使用__block修饰符可以正常修改age,并且age还能保持局部变量

2.3.1 底层实现

  • __main_block_impl_0中,原本是int age,现在变成一个结构体__Block_byref_age_0

  • 使用__Block_byref_age_0结构体来包装age
  • 结构体初始化__Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};,传入的参数相当于这样


__forwarding传的是&age,相当于把自己传进去,指针指向自己(__forwarding的用处后面会再讲到)

3. 总结

  • __block可以用于解决block内部无法修改auto变量值的问题
  • __block不能修饰全局变量、静态变量(static)
  • 编译器会将__block变量包装成一个对象


这个对象对应的是结构体struct __Block_byref_age_0

struct __Block_byref_age_0结构体中有个__forwarding指向自身的指针

4. 拓展

4.1 __block修饰对象

我们再声明一个对象类型的变量

观察底层实现:

  • 使用struct __Block_byref_obj_1结构体来包装obj变量
  • 因为obj是对象类型,牵扯内存管理,所以里面多了copy、dispose方法

4.2 block里面操作NSMutableArray

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        NSMutableArray *mArr = [NSMutableArray array];
        
        ZSXBlock block = ^ {
            [mArr addObject:@"1"];
            [mArr addObject:@"2"];
        };
        
        block();
        NSLog(@"%@", mArr);
    }
    return 0;
}

NSMutableArray实例对象mArr并没有使用__block等方式修饰,而是在 block 里面直接调用addObject方法,并且添加成功了

这是因为,block里面只是使用mArr,并不是修*mArr的值

这种情况下,就没必要使用__block来修饰mArr,反而使用了使用__block修饰符,会让底层结构复杂化

4.3 探索内存

前面文章讲到,使用__block修饰变量后,编译器会将__block变量包装成一个对象,如下

typedef void (^ZSXBlock)(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        __block int age = 10;
        
        ZSXBlock block = ^ {
            age = 20;
            NSLog(@"age is %d", age);
        };
        
        block();
    }
    return 0;
}

将使用struct __Block_byref_age_0包装age变量

那么,怎么确定age = 20;修改的是__Block_byref_age_0结构体中的int age呢

4.3.1 打印比对内存地址

我们将打印age的地址值NSLog(@"%p", &age);,与block结构体里面的age做对比

为了方便调试,我们拷贝底层实现的代码,模拟把block转成结构体形式

typedef void (^ZSXBlock)(void);

struct __Block_byref_age_0 {
  void *__isa;
 struct __Block_byref_age_0 *__forwarding;
 int __flags;
 int __size;
 int age;
};

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)(struct __main_block_impl_0*, struct __main_block_impl_0*);
//  void (*dispose)(struct __main_block_impl_0*);
};

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;
        
        ZSXBlock block = ^ {
            age = 20;
            NSLog(@"age is %d", age);
        };
        
        struct __main_block_impl_0 *blockIml = (__bridge struct __main_block_impl_0 *)block;
        
        NSLog(@"%p", &age);
    }
    return 0;
}

此时如果直接打印age发现没有直接打印出内存地址

我们先打印结构体实现的包装类age,也就是:


接着往下执行,打印age的地址值

会发现地址值不一样

  • 0x00006000020810c0((__Block_byref_age_0 *) age)
  • 0x6000020810d8 (&age)

仔细观察并计算struct __Block_byref_age_0的成员

__Block_byref_age_0里的age地址值就是0x00006000020810d8

因此,age = 20;修改的是__Block_byref_age_0结构体中的int age

苹果这么做,还是为了屏蔽内部实现,开发者age = 20;的操作,看起来就是在访问前面定义的int a = 10;

4.3.2 lldb方式打印

@oubijiexi

重学OC第二十三篇:block 文章目录一、Block是什么?1.1 __block二、Block源码分析 一、Block是什么? int main(int argc, const char * argv[]) { int a = 0; void (^aBlock)(void) = ^{ NSLog(@"%d", a); }; aBlock(); } 用clang转为cpp Block是带有自动变量(局部变量)的匿名函数,但在OC中Block本质上就是一个对象(__main_block_impl_0结构体类比obj 阅读详情

相关推荐

AUTOSAR/汽车行业

AUTOSAR架构及汽车产业链相关介绍

m0_54254513的博客 1万+

OC - Block 详解

1.Block 的使用 Block 是什么? 块,封装了函数调用以及调用环境的 OC 对象, Block 的声明 // 1. @property (nonatomic, copy) void(^myBlock1)(void); // 2.BlockType:类型别名 typedef void(^BlockType)(void); @property (nonatomic, copy) BlockT...

weixin_42350379的博客 1472

3步搞定专利数据爬取:用Selenium绕过国家知识产权局反爬(附完整代码)

本文详细介绍了3种高效获取专利数据的技术方案,重点解析了使用Selenium绕过国家知识产权局反爬的实战方法,并提供了完整代码示例。文章还探讨了Requests+lxml轻量级方案和混合技术策略,帮助开发者根据需求选择最佳数据爬取方案,特别适合需要专利统计数据的开发者参考。

weixin_29029995的博客 1109

OC底层探索(二十三)Block用法及原理

Block的使用 return_type表示返回的对象/关键字等(可以是void,并省略) blockName表示block的名称 var_type表示参数的类型(可以是void,并省略) varName表示参数名称 return_type表示返回的对象/关键字等(可以是void,并省略) blockName表示block的名称 var_type表示参数的类型(可以是void,并省略) Block声明及定义语法 varName表示参数名称 return_type (^blockNam

正在注销账号的博客 1107

手把手教你用CANape调试XCP安全会话:DLL权限管理+四种加密模式实战演示

本文详细介绍了如何使用CANape调试XCP安全会话,重点解析了DLL权限管理的实现与配置方法,并通过实战演示了标定页切换、DAQ测量、STIM旁路和PGM刷写四种典型加密模式的操作流程与调试技巧,帮助工程师深入理解并掌握XCP安全访问机制。

smartcontract5的博客 562

Win11笔记本BIOS设置避坑大全:Secure Boot、RST、UEFI引导,一次讲清

本文详细解析了Win11笔记本BIOS设置中的关键问题,包括Secure Boot、Intel RST和UEFI引导的配置方法,帮助用户避开双系统安装中的常见陷阱。特别针对不同品牌笔记本的BIOS界面差异,提供了实用的操作指南和优化建议,确保系统兼容性和启动效率。

weixin_29251131的博客 687

OC基础 Block

什么是Blocks Blocks是C语言的扩充功能。可以概括为:带有自动变量(局部变量)的匿名函数。 所谓的匿名函数,就是没有名字的函数,在C语言中是不允许的。 在计算机科学中,这种概念也称为:“闭包Closure”、“lambda计算”等。 而且一般而言,函数或者方法里是不能又出现一个函数的,但是Blocks作为一种数据类型,是可以的。通常Blocks用于封装代码。因为即便是写在函数体内部,不去调用它,就不会执行。 Blocks的声明 //无参数 无返回值 void (^block1)(void)

乐事派的专栏 2071

【数据集】CMIP6气候模式数据下载

简介CMIP6及其气候模式数据下载

WW、forever的博客 9352

深入理解 __block 修饰符的底层实现原理

????????关注后回复“进群”,拉你进程序员交流群????????转自:掘金 GDCoderhttps://juejin.cn/post/6942796652717539364在...

<sdffdsfsdfdfs>sfsfsfsdfsdffds</sdfsDS>Fsd 193

2026年医疗AI的可信革命全栈实现(上)

医疗AI可信化:贝叶斯网络与向量数据库的协同架构 本文探讨2026年医疗AI发展的关键转折点——从追求生成能力转向构建可信决策系统。核心观点认为,医疗AI下一阶段的竞争焦点在于不确定性量化和错误传播约束,而非单纯的问答流畅度。研究提出以Python为开发语言、结合向量数据库与贝叶斯网络的混合架构: 技术分层 语言层(LLM):处理自然语言交互与工具调用 证据层(Milvus/Qdrant):实现可审计的临床知识检索 概率层(pgmpy/PyMC):执行贝叶斯推理与风险路由 治理层:确保权限控制与人工复核机制

AllenLV的博客 1149

斯坦福大学3D点云模型下载

本文整理了斯坦福大学10个经典的点云(pcd)及其模型(ply),方便日后使用,欢迎免费下载!

孙悟空 7733

block详解

一、简述: a.每一个方法都是在被调用的时候从硬盘到内存,然后去执行,执行完就消失。在内存的栈区,不需要我们去管理。 b.block,我们成为代码块,它类似一个方法,也是在栈区的。 c.如果我们使用block作为一个对象的属性,则用copy关键词修饰。这样系统会把block的实现拷贝一份到堆区,保证代码块不会提前消亡,使我们对应的属性拥有该block的所有权。 d.block常用于对象之间的通信...

ZHUTAN_123的博客 565

Android Camera MTK框架

Camera简介: 虽然前文说道Android在5.0之后就推出了Camera2 API,但是各个厂家的基本未适配,导致目前市场上大部分机型使用的仍然是Camera1 API。PS:Android 9.0 Google强制要求各个平台厂商(高通/MTK/华为等)支持Camera2,不再兼容Camera1。 ...

weixin_49303682的博客 5506

__block使用

__block说明符 若想在Block语法的表达式中将值赋给在Block语法外面声明的局部变量,需要在自动变量前面附加__block说明符。 __block int val = 0; void (^blk)(void) = ^{val = 1;}; blk(); printf("val = %d\n",val); 复制代码当截获的是Objective-C对象,调用变...

weixin_34249367的博客 371

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Block

01基本认识 block的原理是怎样的?本质是什么? block本质上也是一个OC对象,它内部也有个isa指针 block是封装了函数调用以及函数调用环境的OC对象 封装了函数调用以及调用环境的OC对象 __block的作用是什么?有什么使用注意点? block的属性修饰词为什么是copy?使用block有哪些使用注意? block一旦没有进行copy操作,就不会在堆上 使用注意:循环引...

拾荒者的博客 159

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

OC基础部分

OC基础知识

cai3804007的博客 202

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

iOS底层原理总结 - 探寻block的本质(一)

面试题 block的原理是怎样的?本质是什么? __block的作用是什么?有什么使用注意点? block的属性修饰词为什么是copy?使用block有哪些使用注意? block在修改NSMutableArray,需不需要添加__block? 首先对block有一个基本的认识block本质上也是一个oc对象,他内部也有一个isa指针。block是封装了函数调用以及函数调用环境的OC对象。 ...

小丑鱼 221

block学习笔记

block学习笔记 Blocks 是一个带有自动变量值的匿名函数 1 block语法 完整形式的block语法与一般c语言函数定义,有两点不同 (1)没有函数名 (2)带有 "^" c:   int func(int a)   {     return a;   } block:   ^int (int a)   ...

weixin_30415113的博客 86

iOS Block

Block 什么是block 能够截获自动变量的匿名函数,匿名函数也就是不带函数名的函数 block语法 ^ 返回值类型 (参数列表){ 表达式 } ^int (int test){ return test; }; 当返回值类型为空时我们可以将返回值类型那一项省略或者写成void ^ (参数列表) { 表达式 } ^ void (参数列表) { ...

qq_39696600的博客 340

注意block中变量捕捉的问题

用block可以定义任意的代码片段,将其像对象一样传入另一个方法;它是c级别的语法,和C语言中的函数指针非常相似。在C语言中,函数指针和block写法分别如下  int (* func1)(int a, int b);//c函数指针 返回类型 | 函数名 | 参数 int (^backValue)(intnum);//block 返回类型

NickYang的博客 342

Blocks

--------Blocks 1.Block的简单使用 //返回类型 变量名 参数列表  ^ 插入符 返回类型 参数类型 函数体   int (^blocks)(int a) = ^ int (int a) {}; (1)定义block     int (*p)(int, int); (2)block赋值     block = ^(int age, float height

Snowflake_light的专栏 1350

iOS中block简介-作用域

用blocks可以定义任意的代码片段,将其像对象一样传入另一个方法;它是c级别的语法,和C语言中的函数指针非常相似。在C语言中,函数指针和blocks写法分别如下 int (* func1)(int a, int b);//c函数指针 返回类型 | 函数名 | 参数 void (^backValue)(NSString *strValue);//blocks

2092

iOS block 基本用法总结

iOS Block 经典用法一、Block定义 block定义:是OC中的一种数据类型,可以保存代码,传递参数等,被广泛的运用于iOS开发中。 ^是block的特有标记 block的实现方法的代码实在{}之间 block的定义(如下示例代码): //声明形式 返回值(^块名称)(参数类型 参数名,参数类型 参数名,...) 1.)不带参数的block void(^noArgumentsBlo

Garvey's Blog 1181

OC对象 - Block的变量捕获

oubijiexi。

sharp521的博客 818

OC对象 - Block-对象类型的auto变量

当block内部访问了对象类型的auto变量时如果block是在栈上,将不会对auto变量产生强引用如果block被拷贝到堆上会调用block内部的copy函数copy函数内部会调用函数_Block_object_assign函数会根据auto变量的修饰符strong、weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用如果block从堆上移除会调用block内部的dispose函数dispose函数内部会调用函数。

sharp521的博客 1013

OC对象 - Block内存管理

block变量包装成一个对象,对象对应的结构体如下。,并且在修改值的时候,它也是通过访问包装对象的。在栈上时,他指向的是栈上的自己。复制到堆上后,不管使用栈上的。变量用结构体的指针,堆上的。指向的还是自己本身的指针。,再访问将要操作的变量。使用过程中,是可能从。是指向自己的指针,当。拷贝到堆上后,栈上的。

sharp521的博客 1076
上一篇: iOS开发 - 转源码 - __weak问题解决
下一篇: OC对象 - Block内存管理
aiousee
博客等级 码龄14年 540粉丝 45原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值