消息转发解决NSTimer循环引用

由于NSTimer会保留target的引用计数,所以使用过程中很容易造成循环应用。解决办法很容易,通常会利用block,来实现self的weak化,从而避免self和target相互强引用。具体实现思路和方式,参见
用Block解决NSTimer循环引用

实际上,iOS10的sdk中,官方已经提供了block实现timer的接口:

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;

另一种思路:消息转发

循环引用本质在于相互强引用,无法自主释放。所以防止循环产生,避免闭环产生即可。

消息转发解决NSTimer循环引用_第1张图片
11.png

如图,self强引用timer,timer强引用weakobject,但weakobject弱引用self,即避免了强引用闭环的产生。当外部不在强引用self,内部timer也不会强引用self,引用计数为0,self可以正常释放。
这里要实现的,是如何使对weakobject的发送的消息,转化为self对象来实现。

//.h file
@interface HACWeakObject : HACObject
@property (nonatomic, weak, readonly) HACObject *target;

+ (instancetype)weakObjectWithTarget:(HACObject*)target ;
@end

//.m file
#import "HACWeakObject.h"

@interface HACWeakObject ()
@property (nonatomic, weak, readwrite) HACObject *target;

@end

@implementation HACWeakObject

+ (instancetype)weakObjectWithTarget:(HACObject *)target {
    HACWeakObject *weakObject = [[HACWeakObject alloc] init];
    weakObject.target = target;
    return weakObject;
}

- (id)forwardingTargetForSelector:(SEL)aSelector {
    if (self.target&&[self.target respondsToSelector:aSelector]) {
        return self.target;
    }
    return nil;
}
@end

//use case:
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:[HACWeakObject weakObjectWithTarget:self] selector:@selector(timerFired:) userInfo:nil repeats:YES];

代码,利用message forward的方式,实现了weakobject转发消息到target来实现的过程。当在timer对target,即weakobject发送timerFired消息,weakobject试图处理消息,但没有实现方法所以触发消息转发,首先会+resolveInstanceMethod,weakobject没实现,返回NO,进入到forwardingTargetForSelector,试图转发到其他对象解决。在这里设置处理对象为weak方式保留的self,转到self来响应该消息。从而实现既不保留self,又能够让self处理消息的初衷。
消息转发机制可以参考:
轻松学习之 Objective-C消息转发

补充:
最新发现,即使self的assign属性,如NSInteger类型,当使用block方式,在block中调用时,也无法及时释放,产生循环引用。而assign类型不属于object,无法通过weak方式解决。目前的解决方案是__block后再block内使用,或者改为NSNumber。

你可能感兴趣的:(消息转发解决NSTimer循环引用)