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
实现类似功能。