NSProxy是啥,用来干嘛的

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]; // 触发实际加载

​​优势​​:节省内存,避免提前加载大资源。


⚠️ ​​三、注意事项​​

  1. ​​必须重写这两个方法​​

    objectivec 复制代码
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
        // 返回一个有效的方法签名(否则崩溃)
        return [self.targets.firstObject methodSignatureForSelector:sel];
    }
    
    - (void)forwardInvocation:(NSInvocation *)invocation {
        // 实际转发逻辑
    }
  2. ​​类型欺骗问题​​

    • NSProxy 对象可以伪装成任何类型(如 (UIImage *)proxy),但运行时可能崩溃。
    • 建议用 respondsToSelector:提前校验。
  3. ​​性能开销​​

    • 每次消息转发比直接调用慢 ​10~100倍​,高频调用场景慎用。
  4. ​​内存管理​​

    • 若 targets 是弱引用(如 __weak),需防止对象提前释放。

📊 ​​四、NSProxy vs NSObject 消息转发​​

​​特性​​ NSProxy NSObject 的消息转发
​​设计目的​​ 专为消息转发而生 通用基类,转发是附加能力
​​性能​​ 稍快(无NSObject开销) 略慢
​​使用场景​​ 纯转发代理 需继承其他功能的类
​​初始化​​ 需自定义 init 标准 init

💡 ​​五、经典应用案例​​

  1. ​​Foundation 的 NSDistantObject​​

    用于分布式对象通信(跨进程调用)。

  2. ​​AFNetworking 2.x 的 AFHTTPRequestOperation​​

    用 Proxy 管理网络请求的回调。

  3. ​​RAC 的 RACProxy​​

    实现 ReactiveCocoa 的信号转发。


🌟 ​​总结​​

  • ​​NSProxy 是消息转发的专业户​​,适合:

    ✅ 模拟多继承

    ✅ AOP 无侵入扩展

    ✅ 虚拟化/延迟加载

  • ​​不要用它继承业务逻辑​ ​,普通类还是选 NSObject。

  • ​​Swift 替代方案​​:

    用 Protocol Extension或 Combine的 Publisher实现类似功能。

相关推荐
传奇开心果编程6 小时前
【SwiftUI提高练中学】第13课 实时活动与灵动岛:ActivityKit 实战
学习·ui·ios·swiftui·swift
可乐鸡翅yeah_11 小时前
M3U8 防盗链 URL‑Token 签名,新手开发实操避坑
开发语言·javascript·ios·音视频·safari
传奇开心果编程19 小时前
【SwiftUI入门练中学】第17课 通知与后台任务
学习·ui·ios·swiftui·swift
ii_best1 天前
按键精灵手机端开发安卓版实战:手写一个可最小化、可拖动的「运行日志悬浮窗」(附完整 源码 + 踩坑记录)
android·ios·智能手机·自动化·ai编程·按键精灵
美狐美颜SDK开放平台1 天前
Android与iOS直播APP美颜有什么区别?视频美颜SDK开发详解
android·ios·音视频·视频美颜sdk
传奇开心果编程2 天前
【SwiftUI入门练中学】第7课 动画与手势:让界面更灵动
学习·macos·ui·ios·swiftui·swift
知潮网3 天前
发售刚十天,9999 元的 iPhone 18 Pro 解锁就重启
ios·iphone
2501_916007473 天前
苹果应用商店App Store上架费用标准及原因解析
android·ios·小程序·https·uni-app·iphone·webview
传奇开心果编程3 天前
【SwiftUI娓娓道来】第3课:让界面活起来——交互与动画指南
学习·macos·ui·ios·swiftui·swift