我订阅了这样的信号:
RACSignal *signal = [[RACSignal createSignal:^(... subscriber) {
for (int i = 0; i < 100; i++) {
[subscriber sendNext:[[RACSignal createSignal:^(... subscriber2) {
NSString *string = someFunctionThatTakesALongTime(i);
[subscriber2 sendNext:string];
[subscriber2 sendComplete];
return nil;
}] setNameWithFormat:@"inside signal"]];
}
[subscriber sendComplete];
return nil;
}] setNameWithFormat:@"outside signal"];
int n = 4;
[[signal flatten:n] subscribeNext:^(NSString *string) { ... }];
我希望-flatten:
并行订阅n
个信号。我尝试用-startLazilyWithScheduler:block:
[RACScheduler scheduler]
作为“内部信号”,但这会让我的计算机停止运转。在仪器中,看起来它正在为每个信号创建一个新线程。
此代码的先前版本作为NSOperations添加到NSOperationQueue,NSOperationQueue设置为并行运行最多n
个操作。它有效,但我可以更容易地使用RAC。
我如何从信号信号中-flatten:
n
一次发出信号,以便内部信号分别在相同的n
线程上运行?
=====================================
更新:我正在咆哮错误的树;我的性能问题是由于物理RAM耗尽。我猜想有些物体的寿命太长,导致我的记忆问题。我偶然在某些时候解决了我的内存使用问题,同时重构了更多地使用RAC。我不知道人们是否会从看到我的代码中受益,但现在是:
我使用以下代码消耗了外部信号:
[[[self drawRects] flatten:self.maxProcesses] subscribeNext:^(NSDictionary *result) {
@strongify(self);
NSString *keyString = result[kDrawRectsResultsKeyKey];
self.imagesByLocation[keyString] = result[kDrawRectsResultsImageKey];
self.repsByLocation[keyString] = result[kDrawRectsResultsRepKey];
[self setNeedsDisplayInRect:[result[kDrawRectsResultsRectKey] rectValue]];
}];
要使用更多RAC操作(替换同一类中的其他命令性代码):
// Get the latest zoomed drawing bounds and get the latest imageProvider's latest imagesByLocation
// Skip one of each signal to avoid firing immediately
RACSignal *zoomedDrawingBounds = [RACChannelTo(self, zoomedDrawingBounds) skip:1];
RACSignal *imagesFromImageProvider = [[[RACChannelTo(self, imageProvider) skip:1]
map:^(id<PTWImageProvider> imageProvider) {
return RACChannelTo(imageProvider, imagesByLocation);
}]
switchToLatest];
// Lift the drawing method, getting a signal of signals on each call
RACSignal *drawingSignals = [[self rac_liftSelector:@selector(drawingSignalsForRect:givenImagesByLocations:)
withSignalsFromArray:@[ zoomedDrawingBounds, imagesFromImageProvider, ]]
switchToLatest];
@weakify(self);
// Lift flatten: using maxProcesses so that if maxProcesses changes, the number of signals being
// flatten:ed can change almost immediately.
RACSignal *drawnRectanglesZoomed = [[[[drawingSignals
rac_liftSelector:@selector(flatten:) withSignalsFromArray:@[ RACChannelTo(self, maxProcesses) ]]
switchToLatest]
doNext:^(NSDictionary *result) {
@strongify(self);
// side effects! store the rendered image and its associated image rep
NSString *keyString = result[kDrawRectsResultsKeyKey];
self.imagesByLocation[keyString] = result[kDrawRectsResultsImageKey];
self.repsByLocation[keyString] = result[kDrawRectsResultsRepKey];
}]
map:^(NSDictionary *result) {
// Extract the drawn rect from the results
return result[kDrawRectsResultsRectKey];
}];
RACSignal *drawnRectangles = [[drawnRectanglesZoomed
combineLatestWith:RACChannelTo(self, zoomLevel)]
map:^(RACTuple *tuple) {
// Convert between zoomed and unzoomed coordinates
CGRect zoomedRect = [[tuple first] rectValue];
CGFloat zoomLevel = [[tuple second] floatValue];
CGAffineTransform zoomTransform = CGAffineTransformMakeScale(zoomLevel, zoomLevel);
return [NSValue valueWithRect:CGRectApplyAffineTransform(zoomedRect, zoomTransform)];
}];
// Lift setNeedsDisplayInRect: with results from the drawing signals, so setNeedsDisplayInRect: is called
// as tiles are rendered.
[self rac_liftSelector:@selector(setNeedsDisplayInRect:)
withSignalsFromArray:@[ [drawnRectangles deliverOn:[RACScheduler mainThreadScheduler]] ]];
现在,如果我更新我的工作方法以在后台调度程序上返回冷信号,flatten:
会导致多个信号一次运行,而不会出现问题:
RACSignal *signal = [[RACSignal createSignal:^(... subscriber) {
for (int i = 0; i < 100; i++) {
[subscriber sendNext:[[RACSignal startLazilyWithScheduler:[RACScheduler scheduler] block:^(... subscriber2) {
NSString *string = someFunctionThatTakesALongTime(i);
[subscriber2 sendNext:string];
[subscriber2 sendComplete];
}] setNameWithFormat:@"inside signal"]];
}
[subscriber sendComplete];
return nil;
}] setNameWithFormat:@"outside signal"];
答案 0 :(得分:5)
+[RACScheduler scheduler]
每次调用时都会创建一个新的串行GCD队列,但由于GCD队列与操作系统线程没有直接关系,因此不应该造成任何问题。
相反,问题可能是+flatten:
在之前的信号完全结束之前订阅了新信号(即,这是由旧信号传递的事件发生的)。
你可以通过延迟订阅内部信号来解决这个问题:
RACSignal *workSignal = [[[[RACSignal
// Wait for one scheduler iteration,
return:RACUnit.defaultUnit]
delay:0]
// then actually do the work.
then:^{
return [[RACSignal
createSignal:^(id<RACSubscriber> subscriber2) {
NSString *string = someFunctionThatTakesALongTime(i);
[subscriber2 sendNext:string];
[subscriber2 sendComplete];
return nil;
}]
// Invokes the above block on a new background scheduler.
subscribeOn:[RACScheduler scheduler]];
}]
setNameWithFormat:@"inside signal"];
[subscriber sendNext:workSignal];
然而,这似乎不必要地复杂化。当队列结束时,GCD会自动将你的线程数减少,所以我怀疑这个变化是否真的值得。