多线程延迟类释放



下面我定义了一个解决方案,并使用间隔作为一个定时器在后台线程如下:

@weakify(self)
//IMPORTANT:- Throttle is working exactly the same way debounce works in RX SO DO NOT USE IT.
RACScheduler *bacgroundScheduler = [RACScheduler schedulerWithPriority:RACSchedulerPriorityBackground];
RACSignal *sampler = [RACSignal interval:3 onScheduler: bacgroundScheduler];
enter code here
// updateListenerPositionSubject is a RACReplaySubject.
RACSignal *fallbackSignal = [[RACSignal
merge:@[ self.updateListenerPositionSubject, sampler ]]
takeUntil:[self.updateListenerPositionSubject ignoreValues]];

@weakify(self);
[fallbackSignal subscribeNext:^(id _Nullable x) {
@strongify(self);
[self solutionFallBack];
} error:^(NSError *error) {
NSLog(@"Error: %@", error);
} completed:^{
// to make sure subscription get completed when updateListenerPositionSubject sends complete.  
NSLog(@"Completed");
}];
}

, solutionFallBack函数定义如下:

-(void) solutionFallback {
// block the original solution.
[self.updateListenerPositionSubject sendCompleted];
// bunch of conditions
[self performSwitchWith:shape];
}

In case "solution Fallback"满足条件的视图模型将在一段时间后被释放(可能是30秒或1分钟),这不是很好,特别是我在dealloc中做卸载。

所以我尝试了不同的解决方案,以避免有"样品"。,"ipdateListenerPositionSubject"在不同的线程中,我尝试订阅采样信号,直到回退条件满足如下:

RACScheduler *bacgroundScheduler = [RACScheduler schedulerWithPriority:RACSchedulerPriorityBackground];
RACSignal *rac_viewModelWillDealloc = [self rac_signalForSelector:@selector(performSwitchWith:)];
RACSignal *sampler = [[RACSignal interval:self.sceneSwitchConfiguration.roundDuration onScheduler:bacgroundScheduler] takeUntil: rac_viewModelWillDealloc];
@weakify(self);
[sampler subscribeNext:^(id _Nullable x) {
@strongify(self);
self.backgroundThread = [NSThread currentThread];
[self solutionFallback];
} error:^(NSError *error) {
NSLog(@"Error: %@", error);
} completed:^{
NSLog(@"Completed");
}];

,当我确定解决方案的回退解决条件满足,调用"performSwitchWith"…我正在取消当前的后台线程并切换到另一个线程,如下所示:

@weakify(self);
[self.backgroundThread cancel];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
@strongify(self);
// continue init the new vm process here.
});

因此,当我在主线程中切换要调度的间隔时,所有工作都如预期的那样,并且dealloc立即发生:

RACSignal *sampler = [[RACSignal
interval:self.sceneSwitchConfiguration.roundDuration onScheduler:[RACScheduler mainThreadScheduler]] takeUntil:rac_viewModelWillDealloc];

我想把采样器信号保持在后台线程中,并立即释放类。

NSThreadcancel方法不执行抢占式取消。它所做的只是设置一个布尔值,你可以在其他线程中检查。正如医生所说:

该方法的语义与Operation相同。该方法在接收器中设置状态信息,然后由isCancelled属性反映。支持取消的线程应该定期调用isCancelled方法来确定线程是否已经取消,如果已经取消,则退出。

有关取消和操作对象的更多信息,请参见Operation

无论是取消NSThread,调度工作项,还是操作,你唯一可以取消当前在另一个线程上运行的东西的时候,是当这个线程上的代码被明确地写为支持取消(例如,在计算任务中,定期检查isCancelled状态)。

简而言之,使用cancel方法无法解决您的问题。


如果我理解正确的话,你说的是当从后台线程调用self时,某些东西会持续30秒以上,而不是当你从主线程调用它时。这是一个非常不寻常的场景。

在已经提供的基础上很难说是什么原因导致的。(如果你能准备一个没有这些外部依赖的MCVE来显示这个问题就太好了。)

无论如何,我想提出几点建议:

  1. solutionFallback之前、之中和之后添加日志语句。确认此方法是否耗时较长。这可以帮助你缩小延迟的来源。如果这些方法花费的时间更长,那么这显然就是问题所在,你可以开始诊断为什么它们花费的时间比你预期的要长。如果他们立即返回,那么我们就知道问题出在其他地方。

  2. 我建议打开主线程检查器,如果你还没有。从历史上看,在后台线程上尝试UI更新会导致神秘的延迟,主线程检查器会立即识别这些问题。

    我不认为它会发现任何因果关系,但你也可以暂时打开线程消毒器,并确保你没有看到任何问题。

    在早期诊断内存、线程和崩溃问题中讨论了主线程检查器和TSAN。

  3. 如果您没有及时看到对象被释放,请在这30秒以上的延迟期间按"调试内存图"按钮。(请参阅收集内存使用信息或WWDC视频使用Xcode进行可视化调试。)这将准确地告诉你是什么保持了持久的强引用。如果您打开"Malloc堆栈"特性(在泄漏工具没有显示内存泄漏时如何调试它们中描述)?或者在那个视频中),它不仅会告诉你什么有强引用,还会告诉你强引用最初是在哪里建立的。它显然不能告诉你为什么或在哪里没有删除强引用,但至少它会提供一个强引用列表,你可以从中开始你的分析。

最新更新