问题描述
下面我定义了一个解决方案,并在后台线程中使用间隔作为计时器,如下所示:
@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];
}
如果“解决方案回退”条件满足,视图模型将在一段时间(可能是 30 秒或 1 分钟)后被释放,这并不好,特别是我在 dealloc 中进行卸载。
所以我尝试了不同的解决方案以避免在不同的线程中有“sample”和“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.
});
因此,当我在主线程中切换要安排的时间间隔时,一切都按预期进行,并且立即解除分配:
RACSignal *sampler = [[RACSignal
interval:self.sceneswitchConfiguration.roundDuration onScheduler:[RACScheduler mainThreadScheduler]] takeuntil:rac_viewmodelWillDealloc];
我想将采样器信号保留在后台线程中并立即释放该类。
解决方法
NSThread
cancel
方法不执行抢先取消。它所做的只是设置一个布尔值,您可以在其他线程中检查它。作为the docs say:
此方法的语义与用于 Operation
的语义相同。此方法在接收器中设置状态信息,然后由 isCancelled
属性反映。支持取消的线程应定期调用 isCancelled
方法以确定线程是否实际上已被取消,如果已取消则退出。
有关取消和操作对象的详细信息,请参阅 Operation
。
无论是取消 NSThread
、dispatch work item 还是 operations,您唯一可以取消当前在另一个线程上运行的内容的时间是,该线程上的代码是否明确编写以支持取消(例如,在计算任务中,定期检查 isCancelled
状态)。
简而言之,使用 cancel
方法无法解决您的问题。
如果我理解正确的话,您是说当从后台线程调用时,某些东西在 self
上保持了 30 多秒,但当您从主线程调用它时则不然。这是一个非常不寻常的场景。
根据所提供的内容,很难说是什么导致了这种情况。 (如果您可以准备一个 MCVE 来显示此问题,而无需所有这些外部依赖项,那就太好了。)
无论如何,我建议几点:
-
在
solutionFallback
之前、之中和之后添加日志语句。确认此方法是否需要更长的时间。这有助于您缩小延迟的来源。如果这些方法花费的时间更长,那么这显然是问题所在,您可以自行诊断为什么它们花费的时间比您预期的要长。如果他们立即返回,那么我们知道问题出在其他地方。 -
如果您还没有打开主线程检查器,我建议您打开。从历史上看,在后台线程上尝试 UI 更新会导致神秘的延迟,而主线程检查器会立即识别这些问题。
我不认为它会找到任何因果关系,但您也可以暂时打开线程消毒器,并确保您没有在那里看到任何问题。
主线程检查器和 TSAN 在 Diagnosing Memory,Thread,and Crash Issues Early 中讨论。
-
如果您没有看到对象及时解除分配,请在这 30 多秒的延迟期间按下“调试内存图”按钮。 (请参阅 Gathering Information About Memory Use 或 WWDC 视频 Visual Debugging with Xcode。)这将准确地向您展示是什么保持了挥之不去的强参考。如果您打开“Malloc 堆栈”功能(在 How to debug memory leaks when Leaks instrument does not show them? 或该视频中进行了描述),它不仅会显示强引用的内容,还会显示强引用最初建立的位置。它显然无法告诉您为什么或在哪里没有删除强引用,但至少它会提供一个强引用列表,您可以从中开始分析。