美文网首页开发技术文章
优雅的解决NSTimer循环引用

优雅的解决NSTimer循环引用

作者: 再好一点点 | 来源:发表于2021-12-01 16:14 被阅读0次

    一. 使用NSProxy解决NSTimer、CADisplayLink等循环引用

    如下使用NSTimer如果不做任何处理会导致内存泄露。为了解决selftimer互相强引用问题,可以再某个时间点调用 [self.timer invalidate];self.timer = nil;,这样是可以解决内存泄漏的,但是不太灵活。

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

    使用下面这种方式可以很灵活的解决循环引用问题。

    1. ViewController.m类
    @interface ViewController ()
    @property (strong, nonatomic) NSTimer *timer;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[YHProxy proxyWithTarget:self] selector:@selector(timerTest) userInfo:nil repeats:YES];
    }
    
    - (void)timerTest
    {
        NSLog(@"%s", __func__);
    }
    
    - (void)dealloc
    {
        NSLog(@"%s", __func__);
        [self.timer invalidate];
    }
    
    @end
    
    2. YHProxy类
    #import <Foundation/Foundation.h>
    
    @interface YHProxy : NSProxy
    + (instancetype)proxyWithTarget:(id)target;
    @property (weak, nonatomic) id target;
    @end
    
    #import "YHProxy.h"
    
    @implementation YHProxy
    
    + (instancetype)proxyWithTarget:(id)target
    {
        // NSProxy对象不需要调用init,因为它本来就没有init方法
        YHProxy *proxy = [YHProxy alloc];
        proxy.target = target;
        return proxy;
    }
    
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
    {
        return [self.target methodSignatureForSelector:sel];
    }
    
    - (void)forwardInvocation:(NSInvocation *)invocation
    {
        [invocation invokeWithTarget:self.target];
    }
    @end
    

    NSProxy这个类很简单,他不会进行方法查找,也不会进行动态消息解析,而是直接调用methodSignatureForSelectorforwardInvocation,所以不存在效率低的问题。

    二. NSProxy对象调用isKindOfClass

    1. YHProxy1类
    #import <Foundation/Foundation.h>
    
    @interface YHProxy1 : NSObject
    + (instancetype)proxyWithTarget:(id)target;
    @property (weak, nonatomic) id target;
    @end
    
    #import "YHProxy1.h"
    
    @implementation YHProxy1
    
    + (instancetype)proxyWithTarget:(id)target
    {
        YHProxy1 *proxy = [[YHProxy1 alloc] init];
        proxy.target = target;
        return proxy;
    }
    
    - (id)forwardingTargetForSelector:(SEL)aSelector
    {
        return self.target;
    }
    
    @end
    
    2. main函数
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            ViewController *vc = [[ViewController alloc] init];
            YHProxy *proxy = [YHProxy proxyWithTarget:vc];
            YHProxy1 *proxy1 = [YHProxy1 proxyWithTarget:vc];
            NSLog(@"%d %d",
                  [proxy isKindOfClass:[ViewController class]],
                  [proxy1 isKindOfClass:[ViewController class]]);
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    
    3. 结果:
    1 0
    
    4. 分析

    YHProxy1继承自NSObjectYHProxy1的所有父类和[ViewController class]都不是一种类型,所以结果为0
    [proxy isKindOfClass:[ViewController class]]为什么为1呢?因为NSProxyisKindOfClass内部直接调用了- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel、- (void)forwardInvocation:(NSInvocation *)invocation,修改了target导致的,相当于是vc调用了isKindOfClass,所以[proxy isKindOfClass:[ViewController class]]等于[vc isKindOfClass:[ViewController class]]结果为1。
    通过GNUStep可以看到伪代码。

    相关文章

      网友评论

        本文标题:优雅的解决NSTimer循环引用

        本文链接:https://www.haomeiwen.com/subject/ocvsxrtx.html