欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-03-12 20:25:38
NSTimer、CADisplayLink会对target产生强引用,如果target同时对他们产生强引用,则会发生循环引用。 以NSTimer为例,解决循环引用的问题。 方法1:使用block - (void)viewDidLoad { [super viewDidLoad]; // Do any ......

nstimer、cadisplaylink会对target产生强引用,如果target同时对他们产生强引用,则会发生循环引用。

以nstimer为例,解决循环引用的问题。

方法1:使用block

- (void)viewdidload {
    [super 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) {
        [weakself func];
    }];
}

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

- (void)dealloc
{
    nslog(@"%s",__func__);
    [self.timer invalidate];
}

方法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 = [[proxy1 alloc] init];
    proxy.target = target;
    return proxy;
}

- (id)forwardingtargetforselector:(sel)aselector
{
    return self.target;
}

@end
- (void)viewdidload {
    [super viewdidload];
    // do any additional setup after loading the view.
    
    self.timer = [nstimer scheduledtimerwithtimeinterval:1.0 target:[proxy1 initwithtarget:self] selector:@selector(func) userinfo:nil repeats:yes];
}

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

- (void)dealloc
{
    nslog(@"%s",__func__);
    [self.timer invalidate];
}

方法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 = [proxy2 alloc];
    proxy.target = target;
    return proxy;
}

- (nsmethodsignature *)methodsignatureforselector:(sel)sel
{
    return [self.target methodsignatureforselector:sel];
}

- (void)forwardinvocation:(nsinvocation *)invocation
{
    [invocation invokewithtarget:self.target];
}

@end
- (void)viewdidload {
    [super viewdidload];
    // do any additional setup after loading the view.
    
    self.timer = [nstimer scheduledtimerwithtimeinterval:1.0 target:[proxy2 initwithtarget:self] selector:@selector(func) userinfo:nil repeats:yes];
}

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

- (void)dealloc
{
    nslog(@"%s",__func__);
    [self.timer invalidate];
}

方法3的优点:

执行效率高,无需执行父类的方法搜索过程,直接进行消息转发。

关于nsproxy补充:

通过调用iskindofclass

proxy1 *proxy1 = [proxy1 initwithtarget:self];
proxy2 *proxy2 = [proxy2 initwithtarget:self];

nslog(@"%d",[proxy1 iskindofclass:[viewcontroller class]]);   // 0
nslog(@"%d",[proxy2 iskindofclass:[viewcontroller class]]);   // 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 = [self methodsignatureforselector: _cmd];
    inv = [nsinvocation invocationwithmethodsignature: sig];
    [inv setselector: _cmd];
    [inv setargument: &aclass atindex: 2];
    [self forwardinvocation: inv];
    [inv getreturnvalue: &ret];
    return ret;
}