iOS-OC底层30:NSTimer的使用和强引用的处理
2020-11-30 本文已影响0人
MonKey_Money
1.NSTimer的使用
以timerWithTimeInterval开头的需要加到runloop里,在子线程中还需要让runloop,run起来。
以scheduledTimerWithTimeInterval开头的,不需要添加到runloop中,但是如果在子线程中需要让子线程的runloop,run起来。
我们用一个不常用的举例说明
dispatch_async(dispatch_get_global_queue(0, 0), ^{
const char*methodType = method_getTypeEncoding(class_getInstanceMethod([self class], @selector(fireHome)));
NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:methodType];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
invocation.target = self;
invocation.selector = @selector(fireHome);
self.timer = [NSTimer timerWithTimeInterval:1 invocation:invocation repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
[[NSRunLoop currentRunLoop] run];
});
- (void)fireHome{
num++;
NSLog(@"hello word - %d",num);
}
2.强引用的处理
1. __weak解决强引用 (没有解决)
当我们返回到上一层controller时,我们的timer仍然打印,因为他们之间形成了强引用,推测相互之间形成循环应用,导致无法正常释放
__weak typeof(self) weakSelf = self;
self.timer = [NSTimer timerWithTimeInterval:1 target:weakSelf selector:@selector(fireHome) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
但是当我们退出界面的时候我们的timer仍然打印,这是为什么呢?我们参看文档
The object to which to send the message specified by aSelector when the timer fires. The timer maintains a strong reference to this object until it (the timer) is invalidated.
timer强引用了target,这个指的是,直接对target所指向的内存地址强引用,所以我们用weak,无法解决这个问题。
2.当controller界面退出到上层界面的消除引用(成功)
- (void)didMoveToParentViewController:(UIViewController *)parent{
// 无论push 进来 还是 pop 出去 正常跑
// 就算继续push 到下一层 pop 回去还是继续
if (parent == nil) {
[self.timer invalidate];
self.timer = nil;
NSLog(@"timer 走了");
}
}
- (void)dealloc{
NSLog(@"%s",__func__);
}
从打印结果看。timer 被销毁,dealloc方法被调用。
3.中介者模式不直接使用self(没有解决)
self.target = [[NSObject alloc] init];
class_addMethod([NSObject class], @selector(fireHome), (IMP)fireHomeObjc, "v@:");
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self.target selector:@selector(fireHome) userInfo:nil repeats:YES];
void fireHomeObjc(id obj){
NSLog(@"%s -- %@",__func__,obj);
}
controller能正常释放,但是fireHomeObjc一直在运行,这种方法不可取。
4.TimerWapper (能解决强引用,但是太丑陋)
@interface LGTimerWapper()
@property (nonatomic, weak) id target;
@property (nonatomic, assign) SEL aSelector;
@property (nonatomic, strong) NSTimer *timer;
@end
- (instancetype)lg_initWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo{
if (self == [super init]) {
self.target = aTarget; // vc
self.aSelector = aSelector; // 方法 -- vc 释放
if ([self.target respondsToSelector:self.aSelector]) {
Method method = class_getInstanceMethod([self.target class], aSelector);
const char *type = method_getTypeEncoding(method);
class_addMethod([self class], aSelector, (IMP)fireHomeWapper, type);
self.timer = [NSTimer scheduledTimerWithTimeInterval:ti target:self selector:aSelector userInfo:userInfo repeats:yesOrNo];
}
}
return self;
}
// 一直跑 runloop
void fireHomeWapper(LGTimerWapper *warpper){
if (warpper.target) { // vc - dealloc
void (*lg_msgSend)(void *,SEL, id) = (void *)objc_msgSend;
lg_msgSend((__bridge void *)(warpper.target), warpper.aSelector,warpper.timer);
}else{ // warpper.target
[warpper.timer invalidate];
warpper.timer = nil;
}
}
- (void)lg_invalidate{
[self.timer invalidate];
self.timer = nil;
}
- (void)dealloc{
NSLog(@"%s",__func__);
}
self.timerWapper = [[LGTimerWapper alloc] lg_initWithTimeInterval:1 target:self selector:@selector(fireHome) userInfo:nil repeats:YES];
能正常释放,但是感觉有点太丑了。
5.NSProxy(完美)
@interface LGProxy()
@property (nonatomic, weak) id object;
@end
@implementation LGProxy
+ (instancetype)proxyWithTransformObject:(id)object{
LGProxy *proxy = [LGProxy alloc];
proxy.object = object;
return proxy;
}
-(id)forwardingTargetForSelector:(SEL)aSelector {
return self.object;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel{
return [NSObject instanceMethodSignatureForSelector:@selector(init)];
}
- (void)forwardInvocation:(NSInvocation *)invocation{
}