底层

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{
}
上一篇 下一篇

猜你喜欢

热点阅读