多线程延迟 class 释放

MultiThreaded delaying class deallocation

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

@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];
}

如果“解决方案回退”条件满足,viewmodel 将在一段时间后(可能是 30 秒或 1 分钟)被释放,这不是很好,特别是我在 dealloc 中进行卸载。

所以我尝试了不同的解决方案来避免在不同的线程中有“sample”和“ipdateListenerPositionSubject”,我尝试订阅样本信号并 takeuntil fallback 条件满足如下:

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”的 solutoionFallback 解决方案条件时...我正在取消当前后台线程并移交给另一个线程,如下所示:

@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];

我想在后台线程中保留采样器信号并立即释放 class。

NSThread cancel 方法不执行抢先取消。它所做的只是设置一个布尔值,您可以在其他线程中检查它。作为 the docs say:

The semantics of this method are the same as those used for Operation. This method sets state information in the receiver that is then reflected by the isCancelled property. Threads that support cancellation should periodically call the isCancelled method to determine if the thread has in fact been cancelled, and exit if it has been.

For more information about cancellation and operation objects, see Operation.

是否取消 NSThreaddispatch work item, or operations,您唯一可以取消另一个线程上当前 运行 的内容的情况是该线程上的代码是否已明确编写以支持取消(例如,在计算任务中,定期检查 isCancelled 状态)。

总之,使用cancel方法无法解决您的问题


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

根据所提供的内容,很难说是什么原因造成的。 (如果你能准备一个 MCVE 来证明这个问题而没有所有这些外部依赖性,那就太好了。)

无论如何,我会提出一些建议:

  1. solutionFallback 之前、之中和之后添加日志语句。确认此方法是否需要更长的时间。这有助于缩小延迟源的范围。如果这些方法花费的时间更长,那么这显然是问题所在,您可以着手诊断为什么它们花费的时间比您预期的要长。如果他们立即返回,那么我们就知道问题出在别处。

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

    我不认为它会发现任何因果关系,但您也可以暂时打开线程清理程序,确保您在那里没有发现任何问题。

    Main Thread Checker 和 TSAN 在 Diagnosing Memory, Thread, and Crash Issues Early 中讨论。

  3. 如果您没有及时看到对象释放,请在这 30 秒以上的延迟期间按“调试内存图”按钮。 (参见 Gathering Information About Memory Use or WWDC video Visual Debugging with Xcode.) This will show you precisely what is keeping the lingering strong reference. And if you turn on the “Malloc stack” feature (described in 或该视频),它不仅会向您展示具有强引用的内容,还会向您展示最初建立强引用的位置。它显然不能告诉你为什么或在哪里没有删除强引用,但至少它会提供一个强引用列表,你可以从中开始你的分析。