在iOS中定时器的循环引用会导致定时器无法正常关闭,页面无法正常释放导致内存泄漏。
正常来讲 我们的vc强引用定时器 定时器强引用vc从而导致引用环无法结束,通过中间人的方式可以解决相互之间引用的问题
让中间人弱引用vc 定时器强引用中间人对象 这样就断开了定时器和vc间的循环
大致代码如下:
@interface WeakTarget : NSObject
+ (instancetype)proxyWithTarget:(id)target;
@end
#import "WeakTarget.h"
@interface WeakTarget ()
@property (weak,nonatomic,readonly) id target;
@end
@implementation WeakTarget
- (instancetype)initWithTarget:(id)target{
_target = target;
return self;
}
+ (instancetype)proxyWithTarget:(id)target{
return [[self alloc] initWithTarget:target];
}
+(BOOL)resolveInstanceMethod:(SEL)sel{
return YES;
}
+(BOOL)resolveClassMethod:(SEL)sel{
return YES;
}
-(id)forwardingTargetForSelector:(SEL)aSelector{
if([self respondsToSelector:aSelector]){
return self.target;
}
return [super forwardingTargetForSelector:aSelector];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
return [self.target methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)invocation{
SEL sel = [invocation selector];
if ([self.target respondsToSelector:sel]) {
[invocation invokeWithTarget:self.target];
}
}
- (BOOL)respondsToSelector:(SEL)aSelector{
return [self.target respondsToSelector:aSelector];
}
@end
使用:这里以NSTimer为例,CADisplayLink同理
_timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:[WeakTarget proxyWithTarget:self] selector:@selector(timeInterval) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
/// 开启定时器
[_timer fire];
/// 注意别忘记在dealloc方法中关闭并销毁定时器
if(self.timer.isValid){
[self.timer invalidate];
}
_timer = nil;
大致是用到消息转发的机制,利用这个机会再次重温一下消息转发的流程
runtime会为每个类(不是类的实例)添加一个方法列表,该列表通过hash表实现,方便使用时快速查找,缓存中的方法通过hash查找,
消息传递的过程:
在我们调用一个方法的时候,先从缓存中去查找,看缓存中是否有对应方法选择器的实现,如果命中通过函数指针调用方法,完成一次消息的传递
如果没有命中,会通过当前类的isa指针去查找类的方法列表,看是否有同样名称的方法,若果命中,则通过函数指针调用该方法,结束消息的传递
如果当前类的方法类表中仍然没有找到同名方法,则逐级父类的方法列表中查找,通过当前类对象的[super class]指针查找父类的方法列表,如果命中,则返回,如果未命中,则启动消息转发流程
消息转发流程分为3个环节
1、运行时决议是否有动态添加的方法 通过class_addMethod动态添加方法
/// 类方法
-(bool)resolveClassMethod:(SEL)sel
/// 实例方法
-(bool)resolveInstanceMethod:(SEL)sel
消息转发的第一步,通过判断运行时是否需要动态添加方法,如果动态添加方法,通过class_addMethod添加并返回yes,否则走后续流程
- (void)viewDidLoad {
[super viewDidLoad];
[self performSelector:@selector(foo:)];
}
+ (BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == @selector(foo:)) {
class_addMethod([self class], sel, (IMP)fooMethod, "v@");
return YES;
}
return [super resolveInstanceMethod:sel];
}
void fooMethod(id obj, SEL _cmd){
NSLog(@"Doing foo");
}
2.如果运行时未添加该方法,则走后续流程,判断其他对象是否实现了需要的方法,并返回其他对象
-(id)forwardingTargetForSelector:(SEL)aSelector{
if([self respondsToSelector:aSelector]){
return self.target;
}
return [super forwardingTargetForSelector:aSelector];
}
- (BOOL)respondsToSelector:(SEL)aSelector{
return [self.target respondsToSelector:aSelector];
}
如本例中WeakTarget未实现Interval方法,判断target是否实现了interval方法,如果发现target有实现则返回target对象
3、如果这一步未返回需要实现的其他对象,则走最后一步消息转发流程 通过methodSignatureForSelector和forwardInvocation来实现
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
-(void)forwardInvocation:(NSInvocation *)anInvocation
先通过methodSignatureForSelector获取调用方法的签名,返回NSInvocation对象,可以在该方法中指定某个类来实现某个方法
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
return [self.target methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)invocation{
SEL sel = [invocation selector];
if ([self.target respondsToSelector:sel]) {
[invocation invokeWithTarget:self.target];
}
}
如果forwardInvocation中未成功返回执行方法,则消息转发失败,报错指定方法未找到
标签:sel,return,target,CADisplayLink,self,iOS,aSelector,NSTimer,SEL From: https://www.cnblogs.com/qqcc1388/p/17243685.html