函数参数是block

2024-05-12 17:20
文章标签 block 函数参数

本文主要是介绍函数参数是block,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、函数参数是block定义

在 Objective-C 中,编写两个函数,一个导致循环引用,另一个避免循环引用是一个很好的练习来理解循环引用和如何避免它们。

造成循环引用的例子:

// MyClass.h
@interface MyClass : NSObject
@property (nonatomic, strong) void (^myRetainedBlock)(void);
- (void)setBlockThatRetainsSelf:(void (^)(void))block;
@end// MyClass.m
@implementation MyClass
- (void)setBlockThatRetainsSelf:(void (^)(void))block {// 这里 self 持有 myRetainedBlock,而 myRetainedBlock 又捕获了 selfself.myRetainedBlock = block;block(); // 执行 block
}// MyClass 中的其他方法
- (void)doSomething {NSLog(@"Doing something...");
}@end// 使用 MyClass 和设置 block 的例子
MyClass *myObject = [[MyClass alloc] init];
[myObject setBlockThatRetainsSelf:^{[myObject doSomething]; // 这里 block 捕获了 myObject
}];

在这个例子中,MyClass 的一个实例 myObject 设置了一个 block,这个 block 内部引用了 myObject。由于 MyClass 对这个 block 有一个强引用属性 myRetainedBlock,这造成了一个循环引用,因为 myObject 强引用了 block,而 block 也强引用了 myObject

不造成循环引用的例子:

// MyClass.h
@interface MyClass : NSObject
@property (nonatomic, copy) void (^myBlock)(void);
- (void)setBlockThatDoesNotRetainSelf:(void (^)(void))block;
@end// MyClass.m
@implementation MyClass
- (void)setBlockThatDoesNotRetainSelf:(void (^)(void))block {// 使用弱引用来避免循环引用__weak typeof(self) weakSelf = self;self.myBlock = ^{// 在 block 内部将弱引用升级为强引用__strong typeof(weakSelf) strongSelf = weakSelf;[strongSelf doSomething]; // 使用升级后的强引用};block(); // 执行 block
}// MyClass 中的其他方法
- (void)doSomething {NSLog(@"Doing something...");
}@end// 使用 MyClass 和设置 block 的例子
MyClass *myObject = [[MyClass alloc] init];
[myObject setBlockThatDoesNotRetainSelf:^{[myObject doSomething]; // 这里 block 不捕获 myObject
}];

在这个例子中,为了避免循环引用,我们使用 __weak 来创建 self 的弱引用 weakSelf 然后在 block 内部使用这个弱引用。通过这种方式,block 不会强引用 myObject,避免了循环引用。

请注意,在第二个例子中,即使我们的目的是避免循环引用,传递给 setBlockThatDoesNotRetainSelf: 方法的 block 仍然不应该捕获 myObject。这是因为我们已经在方法内部定义了一个适当的 block,它只捕获 weakSelf。如果我们的 block 参数捕获了 myObject,我们将不得不确保它是以正确的方式(例如使用 weakSelf)来捕获 myObject

二、应用:

在实际的 iOS 开发中,使用带有 block 参数的函数或方法很常见,特别是在处理异步操作、回调、动画、网络请求等场景时。以下是一些具体的情况,说明何时需要正确处理 block 以避免或造成循环引用:

1. 网络请求和回调

当你执行一个网络请求并希望在请求完成时执行某些操作,你通常会提供一个完成回调的 block。如果这个 block 捕获了发起请求的对象,并且这个对象又保留了这个 block(例如,作为一个属性),那么就有可能产生循环引用。

避免循环引用的例子

// 假设你有一个网络请求管理器
@implementation NetworkManager- (void)fetchDataWithCompletion:(void (^)(NSData *, NSError *))completion {NSURLSession *session = [NSURLSession sharedSession];NSURL *url = [NSURL URLWithString:@"https://example.com/data"];__weak typeof(self) weakSelf = self;NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {__strong typeof(weakSelf) strongSelf = weakSelf;if (!strongSelf) {return;}// 处理返回的数据if (completion) {completion(data, error);}// 可能还需要更新一些 strongSelf 的属性或状态}];[task resume];
}@end

在这个例子中,网络请求完成时的 block 没有捕获 self,因此不会造成循环引用。

2. 动画

在 UIKit 中,执行动画时通常会使用 block 来定义动画结束后执行的操作。如果你在动画 block 中捕获了一个视图控制器或视图,并且这个对象又保存了这个 block,那么就可能产生循环引用。

避免循环引用的例子

@implementation MyViewController- (void)animateView:(UIView *)view {__weak typeof(self) weakSelf = self;[UIView animateWithDuration:1.0 animations:^{// 执行一些动画view.alpha = 0.0;} completion:^(BOOL finished) {__strong typeof(weakSelf) strongSelf = weakSelf;// 动画完成后的操作if (finished) {[strongSelf animationDidComplete];}}];
}- (void)animationDidComplete {// 动画完成后的一些操作
}@end

在这个例子中,animateView: 方法中的 block 使用了 weakSelf 来避免捕获 self,从而避免循环引用。

3. 委托和数据源

如果你的对象是另一个对象的委托或数据源,并且这个关系通过 block 定义,如果不小心,这也可能导致循环引用。

避免循环引用的例子

@implementation DataSourceManager- (void)setupDataSourceWithConfigBlock:(void (^)(id config))configBlock {__weak typeof(self) weakSelf = self;self.dataSourceConfigBlock = ^{__strong typeof(weakSelf) strongSelf = weakSelf;id config = [strongSelf createDefaultConfig];if (configBlock) {configBlock(config);}};
}- (id)createDefaultConfig {// 创建和返回默认的数据源配置
}@end

在这个例子中,DataSourceManager 类设置了一个数据源配置 block,该 block 使用了 weakSelf 来避免捕获 self,防止循环引用。

结论

总的来说,当你在一个对象的 block 属性或 block 参数中使用 self 时,你应该注意是否会造成循环引用。在大多数情况下,你应该希望避免循环引用,因为它会阻止对象被正确释放,导致内存泄露。使用 __weak 和 __strong 转换是管理循环引用的常用方法。不过,在特定情况下,如果你希望对象在 block 执行期间保持活跃状态,并且你有清晰的机制来打破这个循环,那么循环引用可能是可接受的。

这篇关于函数参数是block的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/983252

相关文章

[Linux Kernel Block Layer第一篇] block layer架构设计

目录 1. single queue架构 2. multi-queue架构(blk-mq)  3. 问题 随着SSD快速存储设备的发展,内核社区越发发现,存储的性能瓶颈从硬件存储设备转移到了内核block layer,主要因为当时的内核block layer是single hw queue的架构,导致cpu锁竞争问题严重,本文先提纲挈领的介绍内核block layer的架构演进,然

【机器人工具箱Robotics Toolbox开发笔记(二十)】机器人工具箱SerialLink I类函数参数说明

机器人工具箱中的SerialLink表示串联机器人型机器人的具体类。该类使用D-H参数描述,每个关节一组。SerialLink I类包含的参数如表1所示。 表1 SerialLink I类参数 参  数 意    义 参  数 意    义 plot 显示机器人的图形表示 jacobn 工具坐标系中的雅可比矩阵 plot3D 显示机器人3D图形模型 Jacob_dot

block对变量捕获的方式

之前见很多文章对block捕获变量的方法,会进行诸如此类的描述:“block会捕获被引用的变量, 并对其进行copy操作, 因此, 可能会导致其引用计数加1,如果处理不好, 可能因循环引用导致内存泄漏。” 实际上, 这种说法并不严谨。block对变量的捕获, 根据变量类型的不同,会采用不同的捕获方式。 (1)静态或者全局变量, 在block中直接是指针传递的方式传入block中,对其进行的操作

Linux block_device gendisk和hd_struct到底是个啥关系

本文的源码版本是Linux 5.15版本,有图有真相: 1.先从块设备驱动说起 安卓平台有一个非常典型和重要的块设备驱动:zram,我们来看一下zram这个块设备驱动加载初始化和swapon的逻辑,完整梳理完这个逻辑将对Linux块设备驱动模型有深入的理解。 zram驱动加载的时候会调用zram_add函数,源码如下: 1887/*1888 * Allocate and initia

Oracle - ORA-01789: Query block has incorrect number of result columns

一、原因     这个错误一般是在执行表之间的相加(union),相减(minus)等SQL语句时,两个个查询块具有不一致的结果列数所导致的。 二、方案     只要将两段SQL语句的列数调整为一致就可以解决。使用union时,要注意数据库字段的格式要一致,如varchar和nvarchar是不一样的。

ARC下的block导致的循环引用问题解析

引言 使用block已经有一段时间了,感觉自己了解的还行,但是几天前看到CocoaChina上一个关于block的小测试主题:【小测试】你真的知道blocks在Objective-C中是怎么工作的吗?,发现竟然做错了几道,才知道自己想当然的理解是错误的,所以抽时间学习了下,并且通过一些测试代码进行测试,产生这篇博客。 Block简介(copy一段) Block作为C语言的扩展,并不是高新

前端面试:对BFC规范(块级格式化上下文:block formatting context)的理解

块级格式化上下文(BFC)是一个独立的渲染区域,具有特定的布局规则。理解BFC对于前端开发非常重要,因为它影响元素的布局和定位。以下是对BFC的一些关键理解: 定义:BFC是一个HTML文档中的部分区域,内部的元素在该区域内独立于外部元素进行布局。BFC的创建可以通过特定的CSS属性,如overflow(非visible)、display: flow-root、position: absolut

猫猫学iOS 之BLOCK的妙用_利用block实现链式编程

猫猫分享,必须精品 原创文章,欢迎转载。转载请注明:翟乃玉的博客 地址:http://blog.csdn.net/u013357243 一:场景 我们有个对象人,他有两个方法,一个是学习study,一个是跑步run, 这个人有个怪癖,跑完步之后必须学习,为了实现这个方法并且能调用方便,我们让跑步和学习都回返回自己这个对象作为下一次调用的快捷方式,代码如下: 调用: int main(

C++---函数参数的内存开辟

参数类型: 形参:函数名后括号中的变量; 实参:真实传给函数的参数,可以是:常量、变量、表达式、函数等。无论实参是何种类型,调用时,它们必须有确定的值,以便把这些值传送给形参。 调用前: 形参:由于函数没有调用,编译系统没有给函数的形参分配实际内存空间,因此没有实际的参数值。 调用时: 形参:实际调用时,系统会给函数的各形参分配存储空间,同时将实参的值依次传递给各形参,调用结束后,系统

prometheus grafana sql 常用函数参数

1. sum(..) by(instance) (increase()) 以instance分组然后相加        2. sum(..) without(instance) 去除instance,以剩下的标签分组然后相加   3. increase(..[1m])  一分钟内的值    4. rate(..[1m])  一分钟内的值除以60秒,如果是2m, 就除以120