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中…...
UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...
mac:大模型系列测试
0 MAC 前几天经过学生优惠以及国补17K入手了mac studio,然后这两天亲自测试其模型行运用能力如何,是否支持微调、推理速度等能力。下面进入正文。 1 mac 与 unsloth 按照下面的进行安装以及测试,是可以跑通文章里面的代码。训练速度也是很快的。 注意…...
