如何在 Objective-C 中实现 Swift 中的 defer 一样的功能

本文深入解析Swift中的defer机制,一种延迟执行代码块的功能,适用于确保资源的正确释放,如关闭图像上下文或数据库连接。通过代码示例展示了如何使用defer简化成对方法的调用,避免遗忘或重复代码,提升代码的清晰度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是 defer ?

defer 是一种延迟执行机制,就是希望某一段代码能在前面写,但是能够在后面(比如作用域末尾)执行。最在 iOS 开发中,会用到这样一些成对使用的函数,比如 UIGraphicsBeginImageContextUIGraphicsEndImageContext 以及其它很多 CGCF 函数,再如数据库打开后需要关闭。如果这些成对出现的操作之间,业务逻辑比较长,或者需要多次返回,那么就很容易遗忘或者重复写多次,因此编译器提供了清理函数的功能 __attribute__((cleanup(clean_func))) ,而本文即将介绍的 defer 也是基于此功能实现的。

安装

推荐使用 CocoaPods 安装到项目中:

pod 'XZKit/XZKitConstants'

XZKitConstantsXZKit 框架的基础模块,非常轻量级,只有一些常用函数、宏定义和类目。

效果

XZKit.defer运行效果图

代码示例

有了 defer 功能,于是妈妈再也不同担心成对的方法忘记调用了。比如代码绘制图片,以前这么写:

UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0);
// ... 
if (condition1) {
	UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 
	UIGraphicsEndImageContext();
	return image;
}
// ...
if (condition2) {
	UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 
	UIGraphicsEndImageContext();
	return image;
}
// ...
UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 
UIGraphicsEndImageContext(); 
return image;

有了 defer 就可以这么写了:

UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0);
defer(^{
    UIGraphicsEndImageContext();
});
// ... 
if (condition1) { // 条件 condition1 下绘制的图片
	return UIGraphicsGetImageFromCurrentImageContext();
}
// ...
if (condition2) { // 条件 condition2 下绘制的图片
	return UIGraphicsGetImageFromCurrentImageContext();
}
// ...
return UIGraphicsGetImageFromCurrentImageContext();                          

怎么样,写起来清爽了很多有木有!

原理分析

功能 defer 主要用到了 GNU C 编译器提供 __attribute__ 机制,该机制可以给变量、函数、类型添加一些编译属性,优化代码在编译时的行为。比如功能 defer 用到的 __attribute__((cleanup(clean_func))) 这个属性,可以告诉编译器,在变量超出作用域时执行 clean_func 清理函数,该清理函数只接受一个参数,就是它所修饰的变量的指针。这是编译时的行为,编译器在分析代码后,在变量作用域的末尾添加上调用清理函数的操作,并最终编译成二进制序列。
所以分析 XZKit 的源码可以发现,每次 defer 宏调用,实际上是在该作用域定义了一个 block 局部变量,即 defer 中的 block 代码块;并对这个 block 变量添加了编译属性,告诉编译器在该变量超出作用域时,将 block 变量的地址传递给 __xz_defer__ 清理函数执行。
以下是源码,里面涉及一个高级宏 __COUNTER__ 每次调用值自增的宏,以保证每次使用 defer 都能声明一个唯一的变量名。

// .h 文件。
extern void __xz_defer__(void (^*operation)(void));
#undef defer
#define __xz_defer_var_v__(X, Y) X##Y
#define __xz_defer_var_m__(X, Y) __xz_defer_var_v__(X, Y)
#define defer(statements) void(^__xz_defer_var_m__(__xz_defer_var_, __COUNTER__))(void) __attribute__((cleanup(__xz_defer__), unused)) = statements

// .m 文件 
void __xz_defer__(void (^*operation)(void)) {
    (*operation)();
}

建议为 defer 添加 Code Snippet 方便调用,当然你也可以像 XZKit 那样,在 #undef defer 之前声明一个同名函数,只是这样的话,代码提示列表会出现两个 defer 选项。
如果觉得该功能不错的话,上 GitHub 给个 Star 吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值