【iOS】OC中的一些宏

OC中的一些宏

  • OC中的一些宏
    • [1. FOUNDATION_EXPORT](#1. FOUNDATION_EXPORT)
    • [2. Nullability 注解体系](#2. Nullability 注解体系)
    • [3. NS_STRING_ENUM / NS_EXTENSIBLE_STRING_ENUM](#3. NS_STRING_ENUM / NS_EXTENSIBLE_STRING_ENUM)
    • [4. NS_SWIFT_NAME / @objc](#4. NS_SWIFT_NAME / @objc)

OC中的一些宏

1. FOUNDATION_EXPORT

FOUNDATION_EXPORT能够将一个常量暴露给文件外供使用,有区分于C++。

  • 变量:

    • C++/OC: C++/OC中如果想实现变量 能够被外部文件使用,直接在全局作用域定义,外部文件使用时需要使用extern修饰声明这个变量才可以使用
  • 常量:

    • C++:C++中如果想实现常量 能够被外部文件使用,需要在定义 时使用extern修饰,外部文件使用时需要使用extern修饰声明这个常量才可以使用。
    • OC:OC中如果想实现常量 能够被外部文件使用,需要在声明 时使用FOUNDATION_EXPORT修饰,外部文件使用时导入声明这个常量的头文件,这个常量就可以使用。

注意:OC中不能使用FOUNDATION_EXPORT修饰定义,只能修饰声明。C++中可以使用extern修饰定义。

2. Nullability 注解体系

OC没有像SWift的可选类型语法,导致在处理空指针问题时劣于SWift,于是使用这套体系补充OC的空值语义。

整个体系包含 3 个基础注解 + 2 个批量标注宏,覆盖所有指针类型的空值场景:

注解 / 宏 含义 Swift 映射类型 适用场景
_Nonnull 指针必须非空 非可选类型(String 核心参数、必选属性、非空返回值
_Nullable 指针可以为空 可选类型(String? 可选参数、可选属性、可能返回 nil 的方法
_Null_unspecified 指针未指定空值状态(编译器不做校验,默认行为) 隐式可选类型(String! 兼容旧代码,不推荐新代码使用
NS_ASSUME_NONNULL_BEGIN/END 批量标注宏:块内所有未显式标注的指针默认 _Nonnull - 类 / 分类的大部分接口为非空时,减少重复标注
NS_REFINED_FOR_SWIFT (进阶)细化 Swift 混编的空值语义(如 OC 标 _Nonnull,Swift 侧仍可处理 nil) - 混编场景的精细控制

注意:

  • 仅作用于指针类型_Nonnull/_Nullable 只对 OC 对象指针(NSString */UIView *)、C 指针(char *)、Block 指针生效,基本类型(int/CGFloat/BOOL)标注无意义(编译器忽略);
  • 注解是 "约定" 而非 "强制"_Nonnull 仅做编译期静态校验,若通过运行时(如 KVC、强制类型转换)给 _Nonnull 变量赋值 nil,编译器无法拦截,运行时仍可能崩溃;

2.1NS_REFINED_FOR_SWIFT

先说一个场景,OC中定义了一个返回值为非空的方法,Swift中调用OC中的这个方法,但是这个方法返回了一个nil,虽然返回值定义了是非空,但是由于混编到Swift中,_Nonnull类型会被映射成非可选类型(String),此时返回了一个nil,此时就会导致Swift中崩溃。

NS_REFINED_FOR_SWIFT的出现就是为了解决这个问题。

哎,那如果是OC中调用这个方法会出现什么现象呢?

​ : _Nonnull约束的是编译器,运行期返回了nil是不会有什么崩溃报错的,因为OC语言的设计上对空指针的严格性天然的就是小于Swift的,所以Swift中会崩溃,OC中不会,所以才需要NS_REFINED_FOR_SWIFT

修饰是这样的:

objective-c 复制代码
@interface ZZTool : NSObject
// 1. OC 对外暴露:标 _Nonnull,承诺非空(OC 调用者看到的是这个)
- (NSString * _Nonnull)getUserName NS_REFINED_FOR_SWIFT;
@end

使用场景:

  • 当OC中定义一个返回值为_Nonnull的方法,这个方法可能会返回一个nil时,并且SWift中会调用是,最好(必须)使用NS_REFINED_FOR_SWIFT进行修饰。

核心别名对照表(官方定义,语义完全等价):

原注解 / 宏 官方别名(宏定义) 等价关系 备注
_Nonnull nonnull #define nonnull _Nonnull 最常用的别名
_Nullable nullable #define nullable _Nullable 最常用的别名
_Null_unspecified null_unspecified #define null_unspecified _Null_unspecified 极少用,仅兼容旧代码
NS_ASSUME_NONNULL_BEGIN/END 无官方别名 - 无别名,但有对称宏(见下文)
NS_REFINED_FOR_SWIFT 无任何别名 / 变体 -

3. NS_STRING_ENUM / NS_EXTENSIBLE_STRING_ENUM

这两个宏是为了解决:

  • OC 仅支持「整型枚举」(底层只能是数字),语法上无法直接定义 "字符串枚举",传统方式只能用零散的 NSString * 常量模拟,存在类型不安全、语义不清晰的问题;
  • Swift 原生支持「字符串底层的枚举」,而 OC 模拟的字符串常量会被 Swift 映射为裸 String(失去枚举语义),二者混编时体验差、易出错;

NS_STRING_ENUM使用方法:

objective-c 复制代码
// 头文件:可扩展的字符串枚举
typedef NSString *ZZImageExtFormat NS_STRING_ENUM;
FOUNDATION_EXPORT ZZImageExtFormat const ZZImageExtFormatPNG; // 预设:png
FOUNDATION_EXPORT ZZImageExtFormat const ZZImageExtFormatJPG; // 预设:jpg

// 实现文件:赋值
ZZImageExtFormat const ZZImageExtFormatPNG = @"png";
ZZImageExtFormat const ZZImageExtFormatJPG = @"jpg";

// 使用时
// ✅ 合法:用预设的 png
[self processImage:ZZImageFixedFormatPNG];

// ❌ 非法:传自定义的 avif(编译器直接报错!)
[self processImage:@"avif"];

NS_EXTENSIBLE_STRING_ENUM使用方法:

objective-c 复制代码
// 头文件:可扩展的字符串枚举
typedef NSString *ZZImageExtFormat NS_EXTENSIBLE_STRING_ENUM;
FOUNDATION_EXPORT ZZImageExtFormat const ZZImageExtFormatPNG; // 预设:png
FOUNDATION_EXPORT ZZImageExtFormat const ZZImageExtFormatJPG; // 预设:jpg

// 实现文件:赋值
ZZImageExtFormat const ZZImageExtFormatPNG = @"png";
ZZImageExtFormat const ZZImageExtFormatJPG = @"jpg";

// 使用时
// ✅ 合法:用预设的 png
[self processImage:ZZImageExtFormatPNG];

// ✅ 合法:传自定义的 avif(编译器不报错!)
[self processImage:@"avif"];

从上面可以发现NS_STRING_ENUMNS_EXTENSIBLE_STRING_ENUMde 区别就是不可扩展和可扩展。

也就是使用不可扩展的NS_STRING_ENUM,使用这个枚举时只能使用预设的ZZImageExtFormatPNG和ZZImageExtFormatJPG。

如果使用了可扩展的NS_EXTENSIBLE_STRING_ENUM,使用这个枚举时可以传入自定义的字符串值:@"avif"

使用了NS_STRING_ENUMNS_EXTENSIBLE_STRING_ENUM映射到Swift中时,就会被映射为:

swift 复制代码
enum ZZNetworkStatus: String {
  ...
  ...
}

4. NS_SWIFT_NAME / @objc

混编的时候,OC中的方法/变量在Swift中使用时希望能够重定义方法/变量名,就可以使用NS_SWIFT_NAME,反之可以使用@objc

NS_SWIFT_NAME的使用方法:

objective-c 复制代码
// OC中:
@interface ZZPerson : NSObject NS_SWIFT_NAME(Person);
@property (nonatomic, copy) NSString *name;
- (NSString *)getName;
@end

// Swift中使用时
// 直接用 Person,无需写 ZZPerson
let person = Person()
person.name = "张三"
print(person.getName())

@objc的使用方法:

objective-c 复制代码
// Swift 代码
class Person: NSObject {
    // OC 中属性名变为 userName(而非 name)
    @objc(userName) 
    var name: String = ""
}

// OC 代码
person.userName = @"李四"; // 用自定义的 OC 属性名
NSLog(@"%@", person.userName);

注意:重命名后仅能使用重命名后的名称(对应语言侧),原名称会失效,就是OC中重命名后在Swift中只能使用重命名后的名称,不能使用原名称,但是OC中只能使用原名称,反之依然。

相关推荐
qq_316837751 天前
uni.chooseMedia 读取base64 或 二进制
开发语言·前端·javascript
Zoey的笔记本1 天前
2026告别僵化工作流:支持自定义字段的看板工具选型与部署指南
大数据·前端·数据库
小二·1 天前
Python Web 开发进阶实战:混沌工程初探 —— 主动注入故障,构建高韧性系统
开发语言·前端·python
gis开发1 天前
【无标题】
java·前端·javascript
小二·1 天前
Python Web 开发进阶实战:低代码平台集成 —— 可视化表单构建器 + 工作流引擎实战
前端·python·低代码
慧一居士1 天前
Vite 中配置环境变量方法及完整示例
前端·vue.js
天意pt1 天前
Idempotency 幂等性 - 点赞和投票功能
前端·javascript·express
weixin_427771611 天前
cursor 智能commit
前端
努力的小陈^O^1 天前
问题:Spring循环依赖问题排查与解决
java·开发语言·前端
徐_三岁1 天前
127.0.0.1 和 localhost 有什么区别?
前端