NSProxy是啥,用来干嘛的
NSProxy 是 Objective-C 中一个特殊的「替身类」,专门用来转发消息 或模拟多继承 。它不像普通类(如 NSObject)那样有实际功能,而是作为一个「中间人」帮你把消息转给其他对象。以下是它的核心用途和经典场景:
🎭 一、NSProxy 的定位
-
官方定义:
NSProxy是一个抽象基类,不继承自NSObject,但同样遵循<NSObject>协议。 -
核心能力:
转发任何消息(即使自己没实现),类似「消息中转站」。
-
特点:
✅ 轻量(比
NSObject更纯粹)✅ 无
-init方法(需自定义初始化)✅ 必须重写
forwardInvocation:或methodSignatureForSelector:
🛠️ 二、NSProxy 的三大用途
1. 模拟多继承(转发给多个对象)
less
// 定义
@interface MultiProxy : NSProxy
@property (nonatomic, strong) NSArray *targets;
@end
@implementation MultiProxy
- (void)forwardInvocation:(NSInvocation *)invocation {
for (id target in self.targets) {
if ([target respondsToSelector:invocation.selector]) {
[invocation invokeWithTarget:target]; // 逐个调用
}
}
}
@end
// 使用
Dog *dog = [Dog new];
Cat *cat = [Cat new];
MultiProxy *proxy = [MultiProxy alloc];
proxy.targets = @[dog, cat];
[proxy performSelector:@selector(bark)]; // 调用Dog的方法
[proxy performSelector:@selector(meow)]; // 调用Cat的方法
效果 :一个 proxy同时拥有 Dog和 Cat的能力(伪多继承)。
2. 实现AOP(面向切面编程)
less
// 日志代理:在方法调用前后插入日志
@interface LogProxy : NSProxy
@property (nonatomic, weak) id target;
@end
@implementation LogProxy
- (void)forwardInvocation:(NSInvocation *)invocation {
NSLog(@"调用前: %@", NSStringFromSelector(invocation.selector));
[invocation invokeWithTarget:self.target]; // 调用原方法
NSLog(@"调用后: %@", NSStringFromSelector(invocation.selector));
}
@end
// 使用
UserService *service = [UserService new];
UserService *proxy = (UserService *)[[LogProxy alloc] initWithTarget:service];
[proxy login]; // 自动输出调用日志
用途:无侵入式添加日志、性能监控、权限校验等。
3. 延迟加载/虚拟对象
less
// 图片延迟加载代理
@interface LazyImageProxy : NSProxy
@property (nonatomic, strong) Class imageClass;
@property (nonatomic, strong) id realImage;
@end
@implementation LazyImageProxy
- (void)forwardInvocation:(NSInvocation *)invocation {
if (!_realImage) {
_realImage = [_imageClass new]; // 首次调用时创建真实对象
}
[invocation invokeWithTarget:_realImage]; // 转发给真实对象
}
@end
// 使用(直到调用display时才加载图片)
UIImage *proxy = (UIImage *)[[LazyImageProxy alloc] initWithClass:[UIImage class]];
[proxy display]; // 触发实际加载
优势:节省内存,避免提前加载大资源。
⚠️ 三、注意事项
-
必须重写这两个方法
objectivec- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { // 返回一个有效的方法签名(否则崩溃) return [self.targets.firstObject methodSignatureForSelector:sel]; } - (void)forwardInvocation:(NSInvocation *)invocation { // 实际转发逻辑 } -
类型欺骗问题
- NSProxy 对象可以伪装成任何类型(如
(UIImage *)proxy),但运行时可能崩溃。 - 建议用
respondsToSelector:提前校验。
- NSProxy 对象可以伪装成任何类型(如
-
性能开销
- 每次消息转发比直接调用慢 10~100倍,高频调用场景慎用。
-
内存管理
- 若 targets 是弱引用(如
__weak),需防止对象提前释放。
- 若 targets 是弱引用(如
📊 四、NSProxy vs NSObject 消息转发
| 特性 | NSProxy | NSObject 的消息转发 |
|---|---|---|
| 设计目的 | 专为消息转发而生 | 通用基类,转发是附加能力 |
| 性能 | 稍快(无NSObject开销) | 略慢 |
| 使用场景 | 纯转发代理 | 需继承其他功能的类 |
| 初始化 | 需自定义 init |
标准 init |
💡 五、经典应用案例
-
Foundation 的
NSDistantObject用于分布式对象通信(跨进程调用)。
-
AFNetworking 2.x 的
AFHTTPRequestOperation用 Proxy 管理网络请求的回调。
-
RAC 的
RACProxy实现 ReactiveCocoa 的信号转发。
🌟 总结
-
NSProxy 是消息转发的专业户,适合:
✅ 模拟多继承
✅ AOP 无侵入扩展
✅ 虚拟化/延迟加载
-
不要用它继承业务逻辑 ,普通类还是选
NSObject。 -
Swift 替代方案:
用
Protocol Extension或Combine的Publisher实现类似功能。