一、前言:90%开发者都会混淆的两个概念
在 iOS 开发日常工作中,Category(分类) 和 Extension(扩展) 是两个出镜率极高的语法特性。
但绝大多数开发者都存在认知误区:
- 认为「分类和扩展是同一个东西,只是OC和Swift叫法不同」
- 分不清什么时候用Category,什么时候用Extension
- 乱用Extension私有扩展、导致代码冲突、属性覆盖、编译报错
- 不懂底层原理,线上出现 方法覆盖、执行顺序错乱 疑难bug
其实二者底层机制、使用权限、编译逻辑、适用场景完全不同。
一句话前置总结:
Category 是 OC 动态编程特性,主打「动态新增方法、解耦业务、批量增强类能力」
Extension 是 Swift 静态语法特性,主打「补全协议、拆分代码、模块化规整」
本文从零拆解底层原理、核心区别、代码实战、落地规范、高频坑点,彻底终结混淆认知。
二、基础定义:彻底分清两者归属
1. Category(分类)
专属 Objective-C 特性,是 Runtime 动态机制的经典实现。
可以在不修改原类源码、不继承、不创建子类的前提下,动态为已有类(系统类/自定义类)新增方法。
文件后缀:.m / .h
2. Extension(扩展)
主要用于 Swift,是静态编译语法,无Runtime动态特性。
作用是对已有类、结构体、枚举进行静态代码补充、协议补全、代码拆分。
Swift 中无 Category 概念,所有类增强统一用 Extension。
重要纠正:OC 也有 Extension(类扩展/匿名分类),但几乎不用!日常口语中 OC 说的「扩展」全部指 Category,Swift 全部指 Extension。
三、核心本质差异(底层原理级)
这是全文最核心内容,看懂底层,就不会用错场景。
| 对比维度 | Category(OC分类) | Extension(Swift扩展) |
|---|---|---|
| 语言归属 | Objective-C 专属动态特性 | Swift 专属静态语法特性 |
| 执行机制 | Runtime 动态加载,运行时合并方法 | 编译期静态合并,无运行时行为 |
| 能否新增属性 | 只能通过Runtime关联对象间接新增,无ivar | 可直接新增计算属性,不能存存储属性 |
| 能否重写方法 | 可以重写原类方法(高危!会覆盖) | 绝对不能重写原有方法,编译报错 |
| 多分类优先级 | 多个分类重写同一方法,最后编译的分类覆盖前者 | 不支持重写,无覆盖逻辑 |
| 协议支持 | 可实现协议,但不推荐 | 主打协议扩展,Swift模块化核心 |
| 代码拆分能力 | 弱,主要用于方法增强 | 极强,按业务、协议、功能拆分代码 |
四、OC Category 完整实战(代码+场景)
1. 基础用法:系统类能力增强
业务场景:项目高频场景,给 NSString、UIButton、UIImage 新增通用业务方法,不侵入系统源码。
NSString+Valid.h
#import <Foundation/Foundation.h>
@interface NSString (Valid)
// 校验手机号合法性
- (BOOL)isValidPhoneNumber;
// 校验邮箱合法性
- (BOOL)isValidEmail;
@end
NSString+Valid.m
#import "NSString+Valid.h"
@implementation NSString (Valid)
- (BOOL)isValidPhoneNumber {
if (self.length != 11) return NO;
NSString *regex = @"^1[3-9]\d{9}$";
NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
return [pred evaluateWithObject:self];
}
- (BOOL)isValidEmail {
NSString *regex = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}";
NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
return [pred evaluateWithObject:self];
}
@end
2. Category 新增属性(关联对象实战)
Category 无法直接生成成员变量,只能通过 Runtime 关联对象实现属性存储。
#import <UIKit/UIKit.h>
@interface UIButton (Ext)
@property (nonatomic, copy) NSString *btnTag;
@end
#import "UIButton+Ext.h"
#import <objc/runtime.h>
@implementation UIButton (Ext)
- (void)setBtnTag:(NSString *)btnTag {
objc_setAssociatedObject(self, @selector(btnTag), btnTag, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)btnTag {
return objc_getAssociatedObject(self, _cmd);
}
@end
3. Category 核心落地场景(OC项目规范)
- 系统类工具增强:字符串校验、日期格式化、图片处理、按钮点击防重复
- 业务解耦:将独立业务逻辑抽离分类,瘦身主类
- 全局统一UI样式:统一按钮、Label、View样式构建方法
- 埋点、统计通用方法:统一页面点击、曝光埋点
4. Category 高危坑点(必看)
- 禁止重写系统原有方法:如重写
[UIButton init],会全局污染,引发诡异bug - 多个分类新增同名方法,后编译覆盖先编译,方法执行不可控
- 关联对象不支持 KVO、不参与归档,不能当正式属性重度使用
- 分类中不要写复杂初始化逻辑,容易引发加载时序问题
五、Swift Extension 完整实战(代码+场景)
Swift Extension 是工程化核心工具,比 OC Category 更规范、更安全、能力更强。
1. 基础能力扩展
import UIKit
extension String {
// 手机号校验
func isValidPhone() -> Bool {
guard count == 11 else { return false }
let regex = "^1[3-9]\d{9}$"
return NSPredicate(format: "SELF MATCHES %@", regex).evaluate(with: self)
}
}
extension UIButton {
// 快速创建按钮
static func create(title: String, color: UIColor) -> UIButton {
let btn = UIButton(type: .custom)
btn.setTitle(title, for: .normal)
btn.setTitleColor(color, for: .normal)
return btn
}
}
2. Extension 计算属性实战
Swift Extension 支持计算属性,极大简化代码,无存储属性、无内存占用。
extension Date {
// 时间戳字符串
var timeStampStr: String {
return String(format: "%.0f", self.timeIntervalSince1970)
}
// 格式化日期字符串
var ymdStr: String {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
return formatter.string(from: self)
}
}
3. 高阶用法:协议扩展(Swift 核心精髓)
Category 做不到,Extension 专属高阶能力:协议默认实现,统一全局能力。
// 定义协议
protocol ViewLoadable {
func setupUI()
func setupConstraint()
}
// 协议默认实现,所有遵守的类自动拥有能力
extension ViewLoadable where Self: UIView {
func setupUI() {
backgroundColor = .white
layer.cornerRadius = 8
layer.masksToBounds = true
}
func setupConstraint() {}
}
// 直接遵守,无需重复实现
class CustomView: UIView, ViewLoadable {
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
}
4. Extension 核心落地场景
- 代码模块化拆分:一个大VC用多个Extension拆分生命周期、UI、网络、点击事件
- 协议默认实现:统一组件样式、统一业务能力、消除冗余代码
- 系统类能力增强:字符串、日期、数组、字典工具方法
- 类型规范补充:补全枚举、结构体业务方法
5. Extension 核心限制(避坑)
- 绝对不能重写原有方法,编译直接报错,从语法层面杜绝覆盖风险
- 不能添加存储属性,只能用计算属性
- 扩展方法权限高于全局,同名扩展方法会优先调用自身
六、Category vs Extension 选型指南(团队规范)
1. OC 项目:只用 Category
OC 无成熟 Extension 工程化方案,所有工具扩展、类能力增强统一使用 Category,遵循「一个功能一个分类」。
2. Swift 项目:只用 Extension
Swift 抛弃 Category 思维,统一使用 Extension,利用协议扩展实现高阶复用。
3. 跨语言混编规范
- OC 分类可以被 Swift 正常调用
- Swift Extension 无法被 OC 识别
- 混编项目通用工具,优先用 OC Category 保证双向兼容
七、高频面试题+易错点总结
1. 为什么 Category 不能加属性?
类的成员变量内存结构在编译期已经确定,Category 是运行时动态追加方法,无法修改原有内存布局,只能通过关联对象间接存储数据。
2. Category 多个同名方法谁生效?
工程编译顺序靠后分类会覆盖靠前分类,方法执行结果不可控,业务中绝对禁止同名方法。
3. Extension 和 Category 最大区别?
Category 是 Runtime 动态行为,可覆盖方法、动态加载;Extension 是静态编译行为,安全、规范、支持协议扩展,无动态风险。
八、全文总结
-
OC Category:动态、灵活、有风险,主打系统类增强、工具方法封装,需规避方法覆盖、关联对象滥用问题。
-
Swift Extension:静态、安全、规范,主打代码拆分、协议默认实现、工程化治理,是Swift项目模块化核心手段。