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

相关推荐
开心就好20254 小时前
uni-app 本地打包 iOS,在 Windows 上使用 KXBuild 产出 ipa 并上架的完整流程
后端·ios
调试人生的显微镜5 小时前
跨平台抓包工具怎么选?TraceEagle抓包鹰 的四种跨平台能力
后端·ios
2501_915909067 小时前
Ipa Guard 防护上架,混淆、重签名和回归测试怎么做
android·ios·小程序·https·uni-app·iphone·webview
木头分享9 小时前
2026淘宝、京东双11活动全自动脚本丨IOS+安卓
ios
茶底世界之下9 小时前
红花太抢眼,蓝天又想留下:用 Awaking 比较两种减饱和
ios
2501_9160088911 小时前
Windows 上写第一个 SwiftUI App,KXBuild 空工程起步,跑到 iPhone 真机
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程
2501_9159184112 小时前
iOS 上架注意事项有哪些?Appuploader 的四个阶段自查清单
android·ios·小程序·https·uni-app·iphone·webview
黑化旺仔1 天前
【OC】Runtime:类元数据、加载初始化与消息派发
开发语言·macos·ios·objective-c
songgeb1 天前
iOS App支持设置RTL语言的实践
ios·swift·ui kit
软泡芙1 天前
【iOS】swift启动屏显示黑屏
ios