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同时拥有 DogCat的能力(伪多继承)。

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

相关推荐
归辞...1 小时前
「iOS」——内存五大分区
macos·ios·cocoa
HX4362 小时前
MP - List (not just list)
android·ios·全栈
忆江南6 小时前
dyld
ios
归辞...17 小时前
「iOS」——GCD其他方法详解
macos·ios·cocoa
游戏开发爱好者81 天前
没有 Mac,如何上架 iOS App?多项目复用与流程标准化实战分享
android·ios·小程序·https·uni-app·iphone·webview
Digitally1 天前
如何将 iPhone 备份到 Mac/MacBook
macos·ios·iphone
songgeb2 天前
Concurrency in Swift学习笔记-初识
ios·swift
mobsmobs2 天前
Flutter开发环境搭建与工具链
android·flutter·ios·android studio·xcode
SY.ZHOU2 天前
iOS上使用WebRTC推拉流的案例
ios·音视频·cocoa·webrtc