唐伯虎 发表于 2021-6-25 09:43:24

[iOS]定时器NSTimer、CADisplayLink的内存管理

  NSTimer、CADisplayLink会对target产生强引用,如果target同时对他们产生强引用,则会发生循环引用。
  以NSTimer为例,解决循环引用的问题。
  方法1:使用block

- (void)viewDidLoad {
    ;
    // Do any additional setup after loading the view.
   
    __weak typeof(self) weakself = self;
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
      ;
    }];
}

- (void)func
{
    NSLog(@"%s",__func__);
}

- (void)dealloc
{
    NSLog(@"%s",__func__);
    ;
}  方法2:使用NSObject作为中间对象

Proxy1.h

@interface Proxy1 : NSObject
+ (instancetype)initWithTarget:(id)target;
@end
Proxy1.m

@interface Proxy1 ()
@property (nonatomic,weak) id target;
@end

@implementation Proxy1

+ (instancetype)initWithTarget:(id)target
{
    Proxy1 *proxy = [ init];
    proxy.target = target;
    return proxy;
}

- (id)forwardingTargetForSelector:(SEL)aSelector
{
    return self.target;
}

@end
- (void)viewDidLoad {
    ;
    // Do any additional setup after loading the view.
   
    self.timer = selector:@selector(func) userInfo:nil repeats:YES];
}

- (void)func
{
    NSLog(@"%s",__func__);
}

- (void)dealloc
{
    NSLog(@"%s",__func__);
    ;
}  方法3:使用NSProxy作为中间对象

Proxy2.h

@interface Proxy2 : NSProxy
+ (instancetype)initWithTarget:(id)target;
@end
Proxy2.m

@interface Proxy2 ()
@property (nonatomic,weak) id target;
@end

@implementation Proxy2

+ (instancetype)initWithTarget:(id)target
{
    Proxy2 *proxy = ;
    proxy.target = target;
    return proxy;
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
    return ;
}

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

@end
- (void)viewDidLoad {
    ;
    // Do any additional setup after loading the view.
   
    self.timer = selector:@selector(func) userInfo:nil repeats:YES];
}

- (void)func
{
    NSLog(@"%s",__func__);
}

- (void)dealloc
{
    NSLog(@"%s",__func__);
    ;
}  方法3的优点:
  执行效率高,无需执行父类的方法搜索过程,直接进行消息转发。
  关于NSProxy补充:
  通过调用isKindOfClass

Proxy1 *proxy1 = ;
Proxy2 *proxy2 = ;

NSLog(@"%d",]);   // 0
NSLog(@"%d",]);   // 1  proxy1为Proxy1类型,Proxy1继承自NSObject,可以正常处理isKindOfClass方法,所以判断结果为0.
  proxy2为Proxy2类型,Proxy2继承自NSProxy,大部分方法会直接进入消息转发阶段,会改为使用target进行调用,所以判断结果为1.
  通过观察NSProxy的源码发现,该方法直接进行了消息转发。

/**
* Calls the -forwardInvocation: method to determine if the 'real' object
* referred to by the proxy is an instance of the specified class.
* Returns the result.<br />
* NB. The default operation of -forwardInvocation: is to raise an exception.
*/
- (BOOL) isKindOfClass: (Class)aClass
{
    NSMethodSignature    *sig;
    NSInvocation      *inv;
    BOOL            ret;
   
    sig = ;
    inv = ;
    ;
    ;
    ;
    ;
    return ret;
}
页: [1]
查看完整版本: [iOS]定时器NSTimer、CADisplayLink的内存管理