美文网首页Objective - C 底层
Objective - C 内存管理(一)CADisplayLi

Objective - C 内存管理(一)CADisplayLi

作者: 爱玩游戏的iOS菜鸟 | 来源:发表于2020-04-11 20:13 被阅读0次

    首先,在学习内存管理章节之前,我们先看下面几个问题,看能否回答上来?

    1. 使用CADisplayLink、NSTimer有什么注意点?
    2. 介绍下内存的几大区域
    3. 讲一下你对 iOS 内存管理的理解
    4. weak指针的实现原理
    5. ARC 都帮我们做了什么?
    6. autorelease对象在什么时机会被调用release?
    7. 方法里有局部对象, 出了方法后会立即释放吗?

    如果不能回答出来,或者对内存管理的理解不够透彻,就有必要对该章节进行只是的梳理了

    (一)CADisplayLink、NSTimer使用

    • CADisplayLinkNSTimer会对target产生强引用,如果target又对它们产生强引用,那么就会引发循环引用
    • CADisplayLink 设置定时,保证调用频率和屏幕的刷新频率(60PFS)一致,16.6ms一次
    //下面的方法会造成循环引用 内存泄漏
    @property(nonatomic,strong)NSTimer *timer;
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(tickMessage)];
        [self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
        //以scheduled开头的方法 已经以默认模式将其安排在当前Runloop中
        self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(tickMessage) userInfo:nil repeats:YES];
    }
    
    -(void)dealloc{
        [self.timer invalidate];
    }
    

    如何解决这个问题? 通过两种方法进行破环

    1. 使用block (NSTimer的block方法)
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
                [weakSelf tickMessage];
            }];
    
    1. 使用代理对象(通过NSProxy,或自定义对象)
    解决思路:通过一个代理对象,对VC进行弱引用,再由NSTimer强引用,再利用消息转发机制

    上代码:

    //.h
    @interface NNProxy : NSObject
    
    @property(nonatomic, weak) id target;
    + (instancetype)proxyWithTarget:(id)target;
    
    @end
    
    //.m
    //NNProxy为继承自NSObject自定义对象
    +(instancetype)proxyWithTarget:(id)target{
        NNProxy *proxy = [[NNProxy alloc] init];
        proxy.target = target;
        return proxy;
    }
    
    //消息转发
    - (id)forwardingTargetForSelector:(SEL)aSelector{
        return self.target;
    }
    
      self.link = [CADisplayLink displayLinkWithTarget:[NNProxy proxyWithTarget:self] selector:@selector(tickMessage)];
      [self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    
      self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[NNProxy proxyWithTarget:self] selector:@selector(tickMessage) userInfo:nil repeats:YES];
    
    NSProxy

    上面我们自定义了一个NNProxy对象,继承自NSObject,实际上标准库中有一个NSProxy类,比较特殊的是NSProxy并不继承自NSObject,也属于基类

    那么NSProxy有什么作用呢?

    //.h
    @interface ZQProxy : NSProxy
    
    @property(nonatomic, weak) id target;
    + (instancetype)proxyWithTarget:(id)target;
    
    @end
    
    //.m
    //ZQProxy为继承自NSProxy的对象,没有init方法
    +(instancetype)proxyWithTarget:(id)target{
        NSProxy *proxy = [NSProxy alloc];
        proxy.target = target;
        
        return proxy;
    }
    
    //NSProxy 消息转发
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
        return [self.target methodSignatureForSelector:aSelector];
    }
    
    - (void)forwardInvocation:(NSInvocation *)invocation{
        [invocation invokeWithTarget:self.target];
    }
    

    那么,我们从代码大概感觉的出来,NSProxy感觉会更麻烦一点,继承自NSObject更精简一些,那么我们为什么要用NSProxy呢?

    更精简!

    • 继承自NSObject,会先有一个消息查找的过程(在消息转发章节有讲述)
    • NSProxy是专门用于消息转发的,继承自NSProxy,如果没有对应的方法,会直接进入消息转发流程,效率更高。

    下面可以证明上述结论:

      ViewController *vc = [[ViewController alloc]init];
      //继承自NSObject
      NSObjProxy *objProxy = [NSObjProxy proxyWithTarget:vc];
      //继承自NSProxy
      NSSubProxy *subProxy = [NSSubProxy proxyWithTarget:vc];
      NSLog(@"%d,%d",
                  [objProxy isKindOfClass:[ViewController class]],
                  [subProxy isKindOfClass:[ViewController class]]);
    

    结果是: 0 1 为什么结果会不一样呢?
    因为isKindOfClass是NSObject的方法,因此NSObjProxy会先进行一个消息查找,找到该方法,因此并不会走消息转发流程,直接判断两个对象是同一个类,结果为0;而NSSubProxy会直接走消息转发,进而判断为1(通过gnustep对Foundation源码的重写查看NSProxy也可以看出)

    相关文章

      网友评论

        本文标题:Objective - C 内存管理(一)CADisplayLi

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