由于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;
另一种思路:消息转发
循环引用本质在于相互强引用,无法自主释放。所以防止循环产生,避免闭环产生即可。
如图,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。