NSProxy 核心原理、消息机制、多继承、AOP、Timer 解耦、快速转发全解

NSProxy 与 NSObject 本质区别、结构体差异、NSProxy 四大实战场景(多继承、Timer 解耦、AOP 切片、方法快速转发) 。全程深度原理 + 可直接复制的实战代码,适合高级 iOS 开发者进阶、面试、架构优化。

一、开篇:为什么 NSProxy 是 iOS 最被低估的类?

在 iOS 开发中,NSProxy 是一个虚类、代理基类、不继承自 NSObject 的特殊存在。

它的地位:

  • iOS 中唯一不继承自 NSObject 的根类
  • 消息转发机制的终极形态
  • 实现多继承、AOP、解耦、埋点、方法交换的神器
  • 解决 NSTimer / CADisplayLink 循环引用 的最优解

NSObject 是我们日常 99% 类的基类,拥有完整的生命周期、属性、方法、内存管理。

两者的设计目标完全不同:

  • NSObject:实体对象,负责存储、生命周期、功能实现
  • NSProxy:代理对象,负责消息转发、中转、拦截,不存储数据

二、底层核心对比:NSProxy vs NSObject(深度)

1. 继承结构(最本质区别)

plaintext

objectivec 复制代码
NSProxy
  ↳ 根类,不继承任何类
  ↳ 遵守 NSObject 协议

NSObject
  ↳ 根类,继承自...(本身就是根类)
  ↳ 遵守 NSObject 协议

结论:

  • NSProxy ≠ 继承自 NSObject
  • 两者是平级的两个根类
  • 但都遵守 <NSObject> 协议

2. 内存结构(结构体差异)

NSObject 结构体(经典 objc_object)

c

运行

objectivec 复制代码
struct NSObject {
    Class isa;
    // 可添加成员变量、属性
    // 有完整的内存布局
}
  • 有内存
  • 能存属性
  • 能写 init
  • 能添加成员变量

NSProxy 结构体(纯转发,无内存)

c

运行

objectivec 复制代码
struct NSProxy {
    Class isa;
    // 没有额外存储空间
    // 不能添加成员变量
}

NSProxy 设计上就是:

  • 不存储数据
  • 不持有属性
  • 不负责业务
  • 只负责转发消息

3. 消息发送机制差异(最关键)

iOS 方法调用 = objc_msgSend

NSObject 消息路线(完整 4 步)

  1. 找自身方法
  2. 动态解析 resolveInstanceMethod
  3. 快速转发 forwardingTargetForSelector
  4. 慢速转发 methodSignatureForSelector + forwardInvocation

NSProxy 消息路线(直接跳过前 3 步)

  1. 直接进入转发
  2. 不查找自身方法
  3. 不执行动态解析
  4. 直接调用 methodSignatureForSelector

这就是 NSProxy 性能极高的原因!

4. 核心能力总结表

表格

维度 NSObject NSProxy
继承关系 根类 根类(不继承 NSObject)
内存存储 支持属性、成员变量 不支持存储(纯转发)
init 方法 可以写 可以写,但不推荐
消息查找 完整查找流程 直接进入转发
性能 正常 极高(无查找损耗)
用途 实体对象 代理、转发、AOP、多继承
能否解决 Timer 循环引用 非常简单
能否实现多继承 不能 完美实现
能否做 AOP 可以(Method Swizzle) 天然支持(无侵入)

三、Runtime 消息机制深度讲解(为 NSProxy 铺路)

1. 消息发送完整流程

当调用 [obj doSomething]

NSObject 流程:

  1. 查 isa 找类
  2. 查方法缓存
  3. 查方法列表
  4. 父类查找
  5. 动态解析
  6. 快速转发
  7. 慢速转发
  8. 报错 unrecognized selector

NSProxy 流程:

  1. 直接进入 慢速转发

2. 快速转发 vs 慢速转发

快速转发

plaintext

erlang 复制代码
- (id)forwardingTargetForSelector:(SEL)aSelector
  • 性能高
  • 只能转发给一个对象
  • 无法修改方法、参数

慢速转发(NSProxy 专用)

plaintext

erlang 复制代码
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
- (void)forwardInvocation:(NSInvocation *)invocation
  • 能修改 SEL、参数、返回值
  • 能转发给多个对象
  • 能实现 AOP、多继承、埋点
  • NSProxy 完全依赖它

四、NSProxy 核心用法(四大实战场景)

场景 1:实现【多继承】(OC 不支持多继承,NSProxy 完美模拟)

OC 只支持单继承,但业务中经常需要:

  • 一个类拥有 A、B 两个类的能力

NSProxy 可以做到!

实现代码

swift

objectivec 复制代码
@interface MultiProxy : NSProxy
- (instancetype)initWithTargets:(NSArray *)targets;
@end

@implementation MultiProxy {
    NSArray *_targets;
}

- (instancetype)initWithTargets:(NSArray *)targets {
    _targets = targets;
    return self;
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    for (id target in _targets) {
        NSMethodSignature *sig = [target methodSignatureForSelector:sel];
        if (sig) return sig;
    }
    return [super methodSignatureForSelector:sel];
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    for (id target in _targets) {
        if ([target respondsToSelector:invocation.selector]) {
            [invocation invokeWithTarget:target];
            break;
        }
    }
}
@end

使用

plaintext

css 复制代码
A *a = [A new];
B *b = [B new];
id proxy = [MultiProxy alloc] initWithTargets:@[a,b];

[proxy run]; // 自动找 a 或 b 执行

真正的多继承能力!


问题

NSTimer 会强持有 target,ViewController 强持有 Timer → 循环引用,无法释放

传统方案

  • __weak
  • 中介者
  • 封装(不够优雅)

NSProxy 最优解

plaintext

less 复制代码
@interface TimerProxy : NSProxy
+ (instancetype)proxyWithTarget:(id)target;
@end

@implementation TimerProxy {
    id _target;
}

+ (instancetype)proxyWithTarget:(id)target {
    TimerProxy *proxy = [TimerProxy alloc];
    proxy->_target = target;
    return proxy;
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    return [_target methodSignatureForSelector:sel];
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    [invocation invokeWithTarget:_target];
}
@end

使用

plaintext

objectivec 复制代码
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1
                            target:[TimerProxy proxyWithTarget:self]
                            selector:@selector(timerEvent)
                            userInfo:nil
                            repeats:YES];

完美解耦!

  • VC 释放
  • Timer 自动释放
  • 无循环引用
  • 代码极简

场景 3:AOP 切片编程、埋点、日志、性能监控(NSProxy 最强能力)

AOP:不侵入原有代码,统一添加逻辑

例如:

  • 所有点击埋点
  • 所有网络请求日志
  • 所有页面性能统计
  • 方法执行耗时

NSProxy 实现 AOP

plaintext

less 复制代码
@interface AOPProxy : NSProxy
+ (instancetype)proxyWithTarget:(id)target;
@end

@implementation AOPProxy {
    id _target;
}

+ (instancetype)proxyWithTarget:(id)target {
    AOPProxy *proxy = [AOPProxy alloc];
    proxy->_target = target;
    return proxy;
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    return [_target methodSignatureForSelector:sel];
}

- (void)forwardInvocation:(NSInvocation *)invocation {

    // 【切片前】埋点/日志
    NSLog(@"before call: %@", NSStringFromSelector(invocation.selector));

    // 转发
    [invocation invokeWithTarget:_target];

    // 【切片后】统计
    NSLog(@"after call: %@", NSStringFromSelector(invocation.selector));
}
@end

使用

plaintext

ini 复制代码
id obj = [AOPProxy proxyWithTarget:realObj];
[obj doSomething];

自动输出:

plaintext

r 复制代码
before call: doSomething
after call: doSomething

无侵入、无耦合、高性能、可插拔


场景 4:方法快速转发、方法交换、统一拦截

NSProxy 可以:

  • 转发任意方法
  • 修改参数
  • 修改返回值
  • 替换方法实现
  • 统一异常捕获

plaintext

ini 复制代码
- (void)forwardInvocation:(NSInvocation *)invocation {

    // 修改参数
    int arg;
    [invocation getArgument:&arg atIndex:2];
    arg = 100;
    [invocation setArgument:&arg atIndex:2];

    // 执行
    [invocation invokeWithTarget:_target];

    // 修改返回值
    int ret = 200;
    [invocation setReturnValue:&ret];
}

五、NSProxy 与 Method Swizzle 的区别

表格

方式 侵入性 性能 适用
Method Swizzle 高(全局替换) 全局方法替换
NSProxy 无(局部代理) 极高 多继承、AOP、解耦、转发

结论:

  • Swizzle 是暴力替换
  • NSProxy 是优雅转发
  • 大型架构一定优先 NSProxy

六、总结

NSProxy 核心结论

  1. NSProxy 不继承自 NSObject,是平级根类
  2. NSProxy 无内存、无属性、只负责消息转发
  3. NSProxy 直接进入转发流程,性能极高
  4. NSProxy 能实现多继承、解耦 Timer、AOP、埋点、方法转发
  5. NSProxy 是 iOS 最优雅的切面、代理、中介方案

使用场景(必须记住)

  • 多继承模拟
  • NSTimer 解耦
  • AOP 切片、埋点、日志
  • 方法转发、拦截、修改
  • 架构解耦、中介模式
相关推荐
songgeb2 小时前
iOS IAP 本地货币展示:从一个需求到搞清楚 priceLocale
ios·swift
MonkeyKing71557 小时前
iOS Block 底层深度解析:结构、变量捕获、copy逻辑与循环引用本质
ios·objective-c
MonkeyKing7 小时前
iOS 二进制重排与PageZero优化:从原理到实战
ios
MonkeyKing8 小时前
iOS 野指针、僵尸对象与Zombie机制原理详解
ios
UXbot8 小时前
AI一次生成iOS和Android双端原型功能详解
android·前端·ios·kotlin·交互·swift
MonkeyKing71558 小时前
iOS音频时钟、时钟同步与音频时间戳原理详解
ios·objective-c·音视频
Zender Han9 小时前
Flutter Edge-to-Edge 介绍及适配使用指南
android·flutter·ios
Zender Han9 小时前
Flutter 高斯模糊介绍与具体实现
android·flutter·ios