普通视图

发现新文章,点击刷新页面。
今天 — 2025年12月4日掘金 iOS

iOS 知识点 - Category / Extension / Protocol 小合集

作者 齐生1
2025年12月3日 16:27

谈到 OC 基础,错不开的三种机制:Category / Extension / Protocol

它们分别解决了:

  • Category:不修改类源代码、不继承的前提下,给已有类 “添加方法”(组织文件、系统类加功能、AOP 风格 hook 等)。
  • Extention: 在实现文件里补充声明 私有 属性、实例变量。
  • Protocol: 只定义 “接口规范”(方法/属性的声明),不提供实现,用于解耦(代码只依赖协议,不依赖具体类)& 多态(不同类实现同一协议,都可赋给协议限定类型id<Protocol>

Category

  • 概念:category 是一种给已有类(包括系统类)增加实例方法/类方法的机制,不需要子类化,也不需要访问原类源码。

  • 限制

    • 不能直接增加新实例变量(ivar),但是可以通过 关联对象 间接添加 “类似属性” 的存储。
        #import <objc/runtime.h>
    
        static const void *kNameKey = &kNameKey;
    
        @implementation NSObject (Name)
    
        - (void)setName:(NSString *)name {
            objc_setAssociatedObject(self, kNameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
        }
    
        - (NSString *)name {
            return objc_getAssociatedObject(self, kNameKey);
        }
        
        @end
    
    • 不能直接访问原类 pivate 类型的变量/方法(同子类),必须要原类在 .h 中公开声明。
  • 编译后的本质category 在编译后,额外的方法会被编译器 “合并” 到原类的 method_list 中,runtime 加载类时一起注册。

    • 简化过程
      1. 编译:每个 .m 中的 @implementation ClassName (CategoryName) 生成一个 category_t 结构,其中包含:
        • class name
        • instance methods list
        • class methods list
        • protocol list
      2. 程序加载:runtimeload_images 时遍历所有 category
        • 找到对应 class
        • 把 category 的 methods_list 插入到 class 的 methods_list 列表前方。(同名方法覆盖原来的实现)
  • ⚠️注意事项

    • 如果分类与原类(或其他分类)有同名方法,后加载的 method 会 覆盖前面的实现(行为依赖于加载顺序)
  • 经典用途

    • 给类添加方法(NSArray+Utils.h 等)
    • 拆分类的实现,按照功能分块(常用于 swift 代码风格)
    • 方法交换(日志、埋点、hook 等)

runtime 讲解篇:juejin.cn/post/757172…

延伸名词 runtimeload_images(面向切面编程)

  • 概念load_images 是 runtime 的一个内部函数,在 dyld 把一个新的 Mach-O image (主程序 / 动态库 / 插件) 加载进来时,会回调 runtime 的 map_images / load_images 这一整套流程。

  • 接口作用

    • 注册 image 里的 类列表、分类列表、协议列表、选择子 等;
    • 把 category 的 方法/协议 挂载到对应的类上;
    • 收集并按照一定顺序调用 +load 方法(先类,后分类)。

你也可以简化理解成:每当一块儿新的二进制文件被载入进程,runtime 就用 load_images 把这块儿里的 oc 元数据接入到系统里。

延伸名词 AOP(面向切面编程)

含义:一种编程思想,能够在「不改动原有代码逻辑」的前提下,在指定的“切面点”上插入额外逻辑(如埋点、日志、监控、权限校验等)。

核心概念 含义
切点(Pointcut) 想“切入”的位置,比如方法调用前/后
通知(Advice) 在切点执行的额外逻辑(before、after、around)
切面(Aspect) 切点 + 通知 的组合
织入(Weaving) 把这些逻辑动态插入代码执行流程的过程

iOS 实现 AOP 的方法

  1. 方法交换:在交换方法中实现新的逻辑
  2. 消息转发:利用 forwardInvocation:resolveInstanceMethod: 在运行时拦截消息,再“转发”到自己的处理逻辑。
  3. 三方库:Aspects

Extension

  • 概念: 在类的实现被编译前,给它再“补充”一些

    • 方法声明
    • 属性声明
    • 额外 ivar(实例变量)
  • ⚠️注意事项:

    • Extension 必须和 @implementation MyClass 在同一个编译单元或可见范围,这样编译器才会把它当成类定义的一部分,生成 ivar 和访问器方法 (setter/getter)。

Protocol

  • 概念:接口规范与解耦,只写签名、不写实现。
  • 可以包含
    • 实例/类方法
    • Property 声明。

@required@optional 的意义和成本

编译期行为

  • @required:类遵循了这个协议,但是没实现 required 方法 → 编译器 warning
  • @optional:完全由你自己决定是否实现,编译器不强制。

运行时行为

  • 协议本身只是一堆元数据(protocol_t),runtime 存储了:
    • 协议有哪些 @required@optional 的方法;
    • 哪些是实例 / 类方法;
    • 哪些是 property。
  • 但是消息派发是不看协议的:
    • 派发时只看这个对象的方法列表里有没有该 selector;
    • 至于这个 selector 来自哪个协议、是否声明在协议里,派发阶段都不关系。
    /// 因此,在调用时常配合:
    if ([self.delegate respondsToSelector:@selector(doOptionalThing)]) {
        [self.delegate doOptionalThing];
    }
    

协议里的 property 到底是什么?

前置知识:协议只写签名,不写实现。

  • 实际上在协议中声明的 @property (nonatomic, copy) NSString *name; 仅仅等价于 setter、getter 的声明(无实现、无 ivar)。
- (NSString *)name;
- (void)setName:(NSString *)name;

protocol 经典用途

代理模式

  • 例如:vc 处理在 view 上的交互事件,view 通过代理调用 vc 实际处理事件的方法。
/// View.h
@interface View : UIView

@property (nonatomic, weak) id<MyViewDelegate> delegate;

@end

/// View.m
- (void)buttonTapped {
    if ([self.delegate respondsToSelector:@selector(didClickButton:)]) {
        [self.delegate didClickButton:self];
    }
}

/// ViewController.m
@interface ViewController () <MyViewDelegate>
@end

@implementation ViewController

#pragma mark - MyViewDelegate
- (void)didClickButton:(MyView *)view {
    // 处理事件
}
  • 这样就做到了:解耦 + 多态 + 依赖倒置

模块/组件之间的接口抽象 类型约束/API设计

protocol vs 继承

协议 继承
本质 接口集合,不带实现、不带存储 类型扩展机制,带实现、带存储
语义关系 描述 “能做” 某些事情 描述 “就是” 某种事物
数量 一个类可实现 个协议 一个类只能有 个直接父类(多继承例外)
主要用途 解耦、抽象、多态 复用、建立层次结构
编译期检查 检查是否实现 required 方法(warning) 检查 override 签名、类型转换等
运行时检查 conformsToProtocol:
 class_conformsToProtocol
isKindOfClass:
 isMemberOfClass:

延伸知识点 isKindOfClass:isMemberOfClass:

  • isKindOfClass:: 是不是这个类或它的子类
- (BOOL)isKindOfClass:(Class)aClass {
    for (Class c = object_getClass(self); c; c = class_getSuperclass(c)) {
        if (c == aClass) return YES;
    }
    return NO;
}
  • isMemberOfClass:: 是不是这个类本身(不包括子类)
- (BOOL)isMemberOfClass:(Class)aClass {
    return object_getClass(self) == aClass;
}

对于实例对象(instance)

@interface Animal : NSObject
@end

@interface Dog : Animal
@end

Animal *a = [Animal new];
Dog    *d = [Dog new];

// isKindOfClass:
[a isKindOfClass:[Animal class]]; // YES  (Animal 本身)
[a isKindOfClass:[Dog class]];    // NO   (Animal 不是 Dog 家族)

[d isKindOfClass:[Animal class]]; // YES  (Dog 是 Animal 的子类,被认为是 Animal 家族的一员)
[d isKindOfClass:[Dog class]];    // YES  (Dog 本身)

// isMemberOfClass:
[a isMemberOfClass:[Animal class]]; // YES  (a 的 class 恰好是 Animal)
[a isMemberOfClass:[Dog class]];    // NO   (class 是 Animal,不是 Dog)

[d isMemberOfClass:[Animal class]]; // NO   (class 是 Dog,不是 Animal)
[d isMemberOfClass:[Dog class]];    // YES  (class 恰好是 Dog)

对于类对象(class)

/// ❌错误的用法
// [Animal isKindOfClass:Animal]; // 左侧的 “Animal” 被 oc 语法解释为消息接受者,右侧的 “Animal” 会被认作 “类型名”,编译器报错参数异常。
// 

// ✅正确的用法
[Animal isSubclassOfClass:[Animal class]]; // YES (正经用法)
[Animal isKindOfClass:[Animal class]];     // NO  (不正经用法)
  • [Animal isKindOfClass:[Animal class]]; 为什么输出 NO?
    • 首先,在 runtime 讲解中已知,类本身也是对象,类型为 Class
    • 再结合 isKindOfClass: 方法的实现:
      1. object_getClass(self) 开始循环向上查询
      2. self 是类对象本身(class object), 首次查询的结果就是 metaClass object;
      3. class object != metaClass object,已经错过了类对象本身,因此返回 NO。

独立开发者亲测:MLX框架让我的App秒变AI原生!15年iOS老兵的2025新感悟

作者 JZXStudio
2025年12月3日 11:16

大家好,我是K哥,一个写了15年iOS代码的独立开发者。从Objective-C时代一路写到Swift,见证过App Store的黄金十年,也熬过内卷最狠的那几年。但2025年,我第一次感受到——AI真的不是噱头,而是每个iOS开发者都能亲手掌控的生产力革命

这一切,都源于苹果在WWDC25正式力推的 MLX框架


🚀 以前做AI功能?难如登天

过去想在App里加个智能推荐、图像生成或语音理解,要么调用第三方API(贵+慢+隐私风险),要么自己啃PyTorch(iOS端部署?别想了)。作为独立开发者,既没算力也没团队,AI功能基本是“看看就好”。

但MLX彻底改变了游戏规则。


🔥 MLX到底强在哪?亲测三大颠覆点

✅ 1. 本地跑大模型,不联网也能AI原生

MLX专为Apple Silicon优化,M1/M2/M3芯片直接硬件加速。我在Mac mini M2上跑7B参数的文本生成模型,响应速度比某些云API还快!用户数据全程留在设备端,隐私合规不再是难题——这在欧盟DSA和国内个保法时代太重要了。

✅ 2. Swift + Python双支持,老iOS人无缝上手

作为Swift死忠粉,我本担心要重学Python生态。结果MLX同时提供Swift和Python API!我用Swift直接调用预训练模型,几行代码就给我的笔记App加上了“智能摘要”和“灵感扩写”功能。不用改架构,不依赖后端,三天上线AI模块——真·秒变AI原生。

✅ 3. 不仅能推理,还能本地微调!

最震撼的是:MLX支持在Mac上直接训练和微调模型。我用自己积累的用户行为数据(脱敏后)微调了一个小模型,个性化推荐准确率提升40%。以前这得租GPU集群,现在一台Mac搞定,成本趋近于零。


💡 给同行的3条实战建议

  1. 别等“完美模型”:从小场景切入,比如“图片自动打标签”、“输入补全”、“语音转待办事项”,用户感知强,开发量小。
  2. 善用Lazy Computation:MLX的延迟计算特性可大幅减少内存占用,这对iPhone内存敏感场景至关重要。
  3. 关注多模态融合:2025年趋势是“文本+图像+音频”联动。比如用户拍张图,App自动生成图文笔记——MLX全支持!

🌟 写在最后:这是属于独立开发者的黄金时代

曾几何时,我们觉得AI是大厂的游戏。但MLX的出现,把顶级AI能力交到了每一个Mac用户手中——而你我,正是最懂如何把它变成好产品的那群人。

15年iOS开发教会我:工具会变,平台会变,但“解决用户真实问题”的初心不变。而今天,MLX给了我们前所未有的杠杆。

如果你也在做独立App,别犹豫——去Apple Developer官网下载MLX,跑通第一个Demo。你会发现,AI原生,真的只差一行import。


#iOS开发 #AI原生应用 #MLX框架 #独立开发者 #AppleSilicon #WWDC25 #程序员日常 #AI创业 #Swift开发 #2025技术趋势

❌
❌