OC-Block
关于OC中的block作为属性时,为什么要要用copy修饰
@property (nonatomic, copy) void (^completionBlock)(void);
很多文章包括AI都会给出类似结论
- Block 默认分配在栈上,如果没有
copy,当方法退出后,Block 会被销毁。- 使用
copy修饰符确保 Block 存储在堆上,避免栈上 Block 在方法返回后被销毁,确保 Block 在属性中能够正常存活和使用。copy是为了让 Block 在对象的生命周期内持久化,尤其是当 Block 需要在方法执行后继续使用时。
然而,随着技术的迭代优化,这些知识恐怕已经过时了,我们来看下面的demo
@interface ViewController ()@property (nonatomic, copy) void(^demoBolck)(void);@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view.void (^tmpBlock)(void) = ^{NSLog(@"bolck called");};NSLog(@"tmpBlock %@", tmpBlock);_demoBolck = tmpBlock;NSLog(@"demoBolck %@", tmpBlock);_demoBolck();
}@end
输出结果是这样的
tmpBlock <__NSGlobalBlock__: 0x1067cf490>
demoBolck <__NSGlobalBlock__: 0x1067cf490>
bolck called
我们看到,未引用任何外部变量的block初始化之后就是 “__NSGlobalBlock__”,在全局内存中存储,然后被当前页面的demoBlock属性引用,虽然设置了copy,但是并没有复制(内存没变)。因为他已经在全局内存了,生命周期和APP是一致的,后面随时可以调用,没有复制的必要了。我们甚至使用weak修饰这个属性也可以在后续代码中调用block:
@property (nonatomic, weak) void(^demoBolck)(void);
说到这里我们稍微展开一下,根据GPT的回答,block的存储方式有三种,原文大致如下:
- 栈内存:用于存储局部变量和不捕获外部变量的
block。栈上的block会在离开作用域时自动销毁,名字:__NSStackBlock__。- 堆内存:用于存储捕获外部变量的
block,以及所有动态分配的对象,名字:__NSMallocBlock__。- 全局内存:用于存储常量和全局变量,且包括不捕获外部变量的
block,名字__NSGlobalBlock__。
有没有发现,关于栈内存和全局内存的说明是有重复的,理解起来好像是“不捕获外部变量的 block”既可以在全局内存存储也可以在栈内存存储。这里触发了他的知识盲区,反复询问也没说明白。所以我们需要继续自己做实验来验证。
既然说如果block引用了“外部变量”就会自动到堆内存,我们试一下
@interface ViewController ()@property (nonatomic, copy) void(^demoBolck)(void);@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view.NSString *pageName = @"ViewController";void (^tmpBlock)(void) = ^{NSLog(@"bolck called in %@", pageName);};NSLog(@"tmpBlock %@", tmpBlock);_demoBolck = tmpBlock;NSLog(@"demoBolck %@", tmpBlock);_demoBolck();
}@end
打印结果
tmpBlock <__NSMallocBlock__: 0x7b0c000fc3c0>
demoBolck <__NSMallocBlock__: 0x7b0c000fc3c0>
bolck called in ViewController
的确,引用了外部变量之后,block直接被存储到了堆内存(名字是__NSMallocBlock__),这种情况下属性的copy依然没什么卵用,copy、strong都没有什么变化,且后续调用都正常。
还做了其他实验,简单说一下
- 如果block仅引用了一个全局变量,block还会存在全局内存中
- 如果block引用了当前类的一个属性,block会存在堆内存中,和局部变量一致
众多实验中,唯独没有找到block存储在栈内存中的情况,本人孤陋寡闻,望熟悉的大哥不吝赐教。
总结
1、早期的程序设计中,如果仅仅是局部作用的block,大概率是存在栈内存中的,用完就被释放掉了。然后如果被传递给了某个类来引用,需要开发者自己通过设置属性为“copy”类型实现复制到堆内存中,以便启用引用计数来管理其生命周期,所以大家都用copy来修饰block属性。
2、现阶段的环境下(我用的Xcode Version 16.2 (16C5032a),iOS18.1,模拟器),苹果做了优化。直接根据block是否引用变量,以及所引用的变量的生命周期来控制block的存储位置,和我们给的属性修饰(copy、strong)没关系了。
为什么这么做呢?我们使用block的时候,大多数情况不会在当前作用域创建并直接调用,也就是大概率是将来的某个时候回调,那他就不是临时的,就会和我们的一些变量关联起来,需要一起管理其生命周期。如果还是老逻辑,那大概率都会从栈中复制到堆,折腾一次,优化概率远效率浪费的概率。所以这种设计逐渐被抛弃了。我没找到官方文档,仅个人猜测。
接下来
1、低版本的iOS系统我们大概率都要支持几个的,所以当下并不一定所有的版本都启用了这个机制,具体从什么时候变的我不知道,没找到机器。所以还是推荐大家继续使用copy来修饰block属性
2、 如果大家一起探讨这个问题,就要加上版本这个变量,新版本什么样,老版本什么样
相关文章:
OC-Block
关于OC中的block作为属性时,为什么要要用copy修饰 property (nonatomic, copy) void (^completionBlock)(void);很多文章包括AI都会给出类似结论 Block 默认分配在栈上,如果没有 copy,当方法退出后,Block 会被销毁。使用 copy 修…...
关于知识蒸馏的概念原理以及常见方法
1. 概念与原理 知识蒸馏的基本定义 知识蒸馏(Knowledge Distillation) 是一种将模型压缩与迁移学习结合的技术:它利用预先训练好的大模型(通常参数量大、精度高、计算开销大)指导一个更轻量(参数量小、推理速度快)的学生模型进行训练,从而在保持模型精度的同时显著减少…...
C++轻量级桌面GUI库FLTK
C轻量级桌面GUI库FLTK Screenshots - Fast Light Toolkit (FLTK) 这里写个备忘录,可以参考一下....
C++20导出模块及使用
1.模块声明 .ixx文件为导入模块文件 math_operations.ixx export module math_operations;//模块导出 //导出命名空间 export namespace math_ {//导出命名空间中函数int add(int a, int b);int sub(int a, int b);int mul(int a, int b);int div(int a, int b); } .cppm文件…...
PID 算法简介(C语言)
一、简介: PID是比例、积分、微分三个环节的组合,用来进行反馈控制。每个部分都有对应的系数,也就是Kp、Ki、Kd。PID 算法实现这三个部分的计算,然后综合起来得到控制输出。 二、PID控制器结构体: PID控制器结构体:包含PID参数(Kp, Ki, Kd);存储积分项和上一次误差;…...
Java中的继承及相关概念
在 Java 中,继承是一种允许一个类继承另一个类的特性。通过继承,子类可以获取父类的属性和方法,这有助于减少代码冗余并提高代码的可维护性。以下是关于文件内容的相关分析和知识点总结: 一、继承的核心概念 1.继承的语法 Java …...
语言月赛 202308【小粉兔做麻辣兔头】题解(AC)
》》》点我查看「视频」详解》》》 [语言月赛 202308] 小粉兔做麻辣兔头 题目描述 粉兔喜欢吃麻辣兔头,麻辣兔头的辣度分为若干级,用数字表示,数字越大,兔头越辣。为了庆祝粉兔专题赛 #1 的顺利举行,粉兔要做一些麻…...
云原生后端|实践?
云原生(Cloud Native)是一种构建和运行应用程序的方法,它充分利用云计算的优势,包括弹性、可扩展性、高可用性和自动化运维。云原生后端开发通常涉及微服务架构、容器化、持续集成/持续部署(CI/CD)、服务网…...
GrassWebProxy
GrassWebProxy第一版: using System; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; using System.IO; using Newtonsoft.Json;…...
6.Python函数:函数定义、函数的类型、函数参数、函数返回值、函数嵌套、局部变量、全局变量、递归函数、匿名函数
1. 函数定义 Python函数通过def关键字定义。一个函数通常包括函数名、参数列表和函数体。 def greet(name):return f"Hello, {name}!"2. 函数的类型 Python中的函数主要有以下几种类型: 普通函数:具有明确的输入参数和返回值。递归函数&am…...
青少年编程与数学 02-008 Pyhon语言编程基础 22课题、类的定义和使用
青少年编程与数学 02-008 Pyhon语言编程基础 22课题、类的定义和使用 一、类类的定义和使用示例 二、定义1. 类定义语法2. 属性和方法3. 构造器和初始化4. 实例化5. 类变量和实例变量6. 类方法和静态方法7. 继承8. 多态总结 三、使用1. 创建类的实例2. 访问属性3. 调用方法4. 修…...
CosyVoice /F5-TTS /GPT-SoVITS /Fish-Speech 开源语音克隆与文本转语音(TTS)项目的对比整理
四个主流开源语音克隆与文本转语音(TTS)项目的对比整理,基于公开资料与实测反馈总结: 项目CosyVoice F5-TTS GPT-SoVITS Fish-Speech 核心技术双向流式语音合成,支持离线与流式一体化建模基于流匹配的ConvNeXt文本表示…...
MySQL基于binlog和gtid主从搭建方案
MySQL基于binlog和gtid主从搭建方案 一.主库配置 1.1 确认 binlog 是否开启 SHOW VARIABLES LIKE %log_bin%; 1.2 创建日志目录并设置权限 mkdir -p /opt/mysql/log_bin chown -R mysql:mysql /usr/local/mysql chmod -R 755 /usr/local/mysql 1.3 修改 my.cnf 配置文件 …...
5 计算机网络
5 计算机网络 5.1 OSI/RM七层模型 5.2 TCP/IP协议簇 5.2.1:常见协议基础 一、 TCP是可靠的,效率低的; 1.HTTP协议端口默认80,HTTPSSL之后成为HTTPS协议默认端口443。 2.对于0~1023一般是默认的公共端口不需要注册,1024以后的则需…...
Vim跳转文件及文件行结束符EOL
跳转文件 gf 从当前窗口打开那个文件的内容,操作方式:让光标停在文件名上,输入gf。 Ctrlo 从打开的文件返回之前的窗口 Ctrlwf 可以在分割的窗口打开跳转的文件,不过在我的实验不是次次都成功。 统一行尾格式 文本文件里存放的…...
智能理解 PPT 内容,快速生成讲解视频
当我们想根据一版 PPT 制作出相对应的解锁视频时,从撰写解锁词,录制音频到剪辑视频,每一个环节都需要投入大量的时间和精力,本方案将依托于阿里云函数计算 FC 和百炼模型服务,实现从 PPT 到视频的全自动转换࿰…...
【鸿蒙开发】第二十四章 AI - Core Speech Kit(基础语音服务)
目录 1 简介 1.1 场景介绍 1.2 约束与限制 2 文本转语音 2.1 场景介绍 2.2 约束与限制 2.3 开发步骤 2.4 设置播报策略 2.4.1 设置单词播报方式 2.4.2 设置数字播报策略 2.4.3 插入静音停顿 2.4.4 指定汉字发音 2.5 开发实例 3 语音识别 3.1 场景介绍 3.2 约束…...
Java/Kotlin双语革命性ORM框架Jimmer(一)——介绍与简单使用
概览 Jimmer是一个Java/Kotlin双语框架 包含一个革命性的ORM 以此ORM为基础打造了一套综合性方案解决方案,包括 DTO语言 更全面更强大的缓存机制,以及高度自动化的缓存一致性 更强大客户端文档和代码生成能力,包括Jimmer独创的远程异常 …...
番外02:前端八股文面试题-CSS篇
一:CSS基础 1:CSS选择器及其优先级 2:display的属性值及其作用 属性值作用none元素不显示,并且会从文档流中移除block块类型,默认元素为父元素宽度,可设置宽高,换行显示inline行内元素类型&a…...
Redis Copilot:基于Redis为AI打造的副驾工具
我们最近发布了Redis Copilot,以帮助开发者更快地使用Redis构建应用。我们的使命是使应用程序快速运行,并简化构建过程。为此,Redis Copilot作为您的AI助手,能够让您更迅速地完成与Redis相关的任务。您今天就可以在Redis Insight中…...
一文读懂水面无人艇:每个硬件模块到底负责什么
目录 一、水面无人艇完整系统 二、硬件搭配负责哪些功能 2.1 艇体模块:决定“能不能稳、能不能装、能不能扛风浪” 2.2 动力与航行执行模块:决定“怎么动” 2.3 导航传感器模块:决定“我现在在哪、朝哪、跑多快” 1)GPS / 北…...
从订餐流程到并发编程:Petri网中的‘库所’与‘变迁’到底在模拟什么?
从订餐流程到并发编程:Petri网中的‘库所’与‘变迁’到底在模拟什么? 想象一下,你正在用手机订外卖:选择菜品、下单支付、等待制作、骑手配送——这个看似简单的流程背后,隐藏着一个精妙的系统状态转换模型。这正是Pe…...
dynamic-datasource JVM调优:提升多数据源性能的7个实用技巧
dynamic-datasource JVM调优:提升多数据源性能的7个实用技巧 【免费下载链接】dynamic-datasource dynamic datasource for springboot 多数据源 动态数据源 主从分离 读写分离 分布式事务 项目地址: https://gitcode.com/gh_mirrors/dy/dynamic-datasource …...
DeepSeek-R1-Distill-Qwen-1.5B响应慢?函数调用优化实战解决方案
DeepSeek-R1-Distill-Qwen-1.5B响应慢?函数调用优化实战解决方案 你是不是也遇到过这种情况:好不容易在本地部署了DeepSeek-R1-Distill-Qwen-1.5B这个“小钢炮”模型,结果发现函数调用时响应特别慢?明明官方说RTX 3060能跑200 to…...
OpenClaw自动化办公:nanobot镜像处理Excel与PPT文件
OpenClaw自动化办公:nanobot镜像处理Excel与PPT文件 1. 为什么选择OpenClaw处理办公文档? 上周五下午5点,当我面对第7个需要合并的Excel报表时,手指已经因为重复的复制粘贴动作开始发麻。作为团队里负责月度数据汇总的"表哥…...
mmsegmentation训练策略调优全攻略:从学习率预热到迭代次数计算
mmsegmentation训练策略调优实战:从参数配置到显存优化 在图像分割领域,mmsegmentation框架因其模块化设计和丰富的预训练模型而广受欢迎。但真正决定模型性能上限的,往往是那些容易被忽视的训练策略细节。本文将带您深入AdamW优化器的参数微…...
用51单片机+无源蜂鸣器播放《两只老虎》完整教程(附代码与乐理速成)
用51单片机驱动无源蜂鸣器演奏《两只老虎》全流程解析 第一次听到单片机播放音乐时,那种"机器唱歌"的奇妙感至今难忘。作为电子爱好者入门必备的趣味项目,用蜂鸣器演奏音乐不仅能巩固定时器、中断等核心知识,更能将枯燥的理论转化为…...
开源工具wxappUnpacker:微信小程序逆向解析实战指南
开源工具wxappUnpacker:微信小程序逆向解析实战指南 【免费下载链接】wxappUnpacker 项目地址: https://gitcode.com/gh_mirrors/wxappu/wxappUnpacker 模块一:工具定位与价值——小程序开发的逆向工程利器 完成本节学习后你将能够:…...
OpenClaw故障排查大全:GLM-4.7-Flash接口超时与网关启动失败
OpenClaw故障排查大全:GLM-4.7-Flash接口超时与网关启动失败 1. 问题背景与典型症状 最近在本地部署OpenClaw对接GLM-4.7-Flash模型时,遇到了两个棘手问题:接口调用频繁超时和网关服务启动失败。作为一个习惯用技术解决实际问题的开发者&am…...
5步实现Switch控制器PC全功能适配:从连接到精通的设备适配指南
5步实现Switch控制器PC全功能适配:从连接到精通的设备适配指南 【免费下载链接】BetterJoy Allows the Nintendo Switch Pro Controller, Joycons and SNES controller to be used with CEMU, Citra, Dolphin, Yuzu and as generic XInput 项目地址: https://gitc…...
