4

次のように作成されたシグナルをサブスクライブします。

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]ましたが、コンピューターが停止します。Instrumentsでは、シグナルごとに新しいスレッドを作成しているようです。

このコードの以前のバージョンは、NSOperations として 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"];
4

1 に答える 1

5

+[RACScheduler scheduler]呼び出されるたびに新しいシリアル GCD キューを作成しますが、GCD キューは OS スレッドと直接関係がないため、それだけで問題は発生しません。

代わりに、問題はおそらく、+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 は自動的にスレッド カウントを元に戻します。そのため、この変更が実際に価値があるかどうかは疑問です。

于 2013-09-26T22:26:21.887 に答える