通过NSProxy解决NSTimer循环引用

56 阅读1分钟

NSProxy和NSObject一样,也是一个基类,是一个实现协议的基类。

@class NSMethodSignature, NSInvocation;

NS_ASSUME_NONNULL_BEGIN

NS_ROOT_CLASS
@interface NSProxy <NSObject> {
    __ptrauth_objc_isa_pointer Class    isa;
}

+ (id)alloc;
+ (id)allocWithZone:(nullable NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
+ (Class)class;

- (void)forwardInvocation:(NSInvocation *)invocation;
- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel NS_SWIFT_UNAVAILABLE("NSInvocation and related APIs not available");
- (void)dealloc;
- (void)finalize;
@property (readonly, copy) NSString *description;
@property (readonly, copy) NSString *debugDescription;
+ (BOOL)respondsToSelector:(SEL)aSelector;

- (BOOL)allowsWeakReference API_UNAVAILABLE(macos, ios, watchos, tvos);
- (BOOL)retainWeakReference API_UNAVAILABLE(macos, ios, watchos, tvos);

// - (id)forwardingTargetForSelector:(SEL)aSelector;

@end

下面是具体实现:

创建一个继承自NSProxy的类HHProxy,并在里面实现以下方法:

@interface HHProxy : NSProxy

+(instancetype)proxyWithTarget:(id)target;

@property(nonatomic,weak)id target;

@end

@implementation HHProxy

+(instancetype)proxyWithTarget:(id)target{

HHProxy *proxy = [HHProxy alloc];

proxy.target = target;

return proxy;

}

-(NSMethodSignature *)methodSignatureForSelector:(SEL)sel{

return [self.target methodSignatureForSelector:sel];

}

-(void)forwardInvocation:(NSInvocation *)invocation{

[invocation invokeWithTarget:self.target];

}

注意:上述代码中target用weak修饰,这样才可以避免循环引用。

timer通过HHProxy调用:

 self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[HHProxy proxyWithTarget:self] selector:@selector(eat) userInfo:nil repeats:YES];

这样就可以避免循环引用了。且NSProxy的效率最高,为什么这么说呢:

通常我们一般的方法调用都是通过objc_msgSend来进行的,然后经过消息发送、动态方法解析、消息转发这三个步骤,而通过代码进行测试,发现继承NSProxy的类,直接执行了methodSignatureForSelector方法,这样就直接进入了消息转发阶段,前两个阶段都省去了,所以效率更高。