Objc-C:RACObserve 快速变化的值(AFHTTPRequestOperation 的 RACSignal)
Objc-C: RACObserve rapidly changing values (RACSignal for AFHTTPRequestOperation)
我正在使用反应式 cocoa 从服务器创建下载文件。我有一个 DownloadMapFileOperation.m 文件,进度为 属性(进度值、currentBytes、totalBytes)。它在我的下一个 RACSignal
方法和浮动进度中变化非常快(这是不必要的,但是
@property (nonatomic, strong, readwrite) NSMutableDictionary *progressDictionary;
@property (nonatomic, assign, readwrite) float progress;
和RACSignal
创建方法
- (RACSignal *)signalForDownloadMap:(Place *)place
{
return [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSString *mapUrlString = [NSString stringWithFormat:@"http://myurl/%@", place.mapFileName];
NSURL *url = [NSURL URLWithString:mapUrlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
_downloadFileOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:[url lastPathComponent]];
[_downloadFileOperation setOutputStream:[NSOutputStream outputStreamToFileAtPath:fullPath append:NO]];
[_downloadFileOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation , id response)
{
NSLog(@"Downloaded via compl block");
[subscriber sendNext:nil];
[subscriber sendCompleted];
} failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
[subscriber sendError:error];
}];
@weakify(self)
self.progressDictionary = [[NSMutableDictionary alloc] init];
self.progressDictionary[@"progress"] = @(0);
self.progressDictionary[@"current"] = @(0);
self.progressDictionary[@"total"] = @(0);
self.progress = @(0);
[_downloadFileOperation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead)
{
@strongify(self)
self.progressDictionary[@"current"] = @(totalBytesRead);
self.progressDictionary[@"total"] = @(totalBytesExpectedToRead);
self.progressDictionary[@"progress"] = @((float)totalBytesRead / (float)totalBytesExpectedToRead);
self.progress = @((float)totalBytesRead / (float)totalBytesExpectedToRead);
self.progress = @((float)totalBytesRead / (float)totalBytesExpectedToRead);
}];
[_downloadFileOperation start];
return nil;
}] filter:^BOOL(id value) {
return !(.mapFileName == nil);
}]
deliverOn:[RACScheduler mainThreadScheduler]
];
}
我只是想通过 RACObserve
在我的 ViewController 中显示我的进度。
[[[DownloadMapFileOperation sharedInstance] signalForDownloadMap:self.place]
subscribeNext:^(AFHTTPRequestOperation *operation) {
NSString *alertMessage = @"Download complete";
[[[UIAlertView alloc] initWithTitle:@"Complete" message:alertMessage delegate:self cancelButtonTitle:@"Close" otherButtonTitles: nil] show];
downloadMapView.progressLabel.text = @"Download Complete";
}];
[[[DownloadMapFileOperation sharedInstance] signalForDownloadMap:self.place] subscribeError:^(NSError *error) {
[[[UIAlertView alloc] initWithTitle:@"Error" message:[error localizedDescription] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil] show];
}];
RAC(downloadMapView, progressView.progress) = [RACObserve([DownloadMapFileOperation sharedInstance], progress)
map:^id(id value) {
return value;
}];
RAC(downloadMapView, progressLabel.text) = [RACObserve([DownloadMapFileOperation sharedInstance], progressDictionary)
map:^id(NSDictionary *value) {
return [value[@"progress"] isEqual: @(1)] ? @"Download Complete" : [NSString stringWithFormat:@"Downloaded %@ of %@", value[@"current"], value[@"total"]];
}];
但我的进度视图有时会从 60% 跳到 50%(或 45 - 35 等),并且 NSLog(@"Downloaded via compl block");
在进度达到 100% 之前显示(有时达到并从大约 70% 开始)。
如果我使用 progressDictionary
(就像第二个 RACObserve
)- 它不起作用(显示 0 进度和下载 0,共 0)
已编辑
我也尝试在我的 DownloadMapFileOperation.m 文件中添加暂停、恢复、取消下载操作的方法。
- (RACSignal *)signalForPauseDownloadMap
{
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[_downloadFileOperation pause];
[subscriber sendNext:nil];
[subscriber sendCompleted];
return nil;
}];
}
- (RACSignal *)signalForResumeDownloadMap
{
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[_downloadFileOperation resume];
[subscriber sendNext:nil];
[subscriber sendCompleted];
return nil;
}];
}
- (RACSignal *)signalForCancelDownloadMap
{
return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[_downloadFileOperation cancel];
[subscriber sendNext:nil];
[subscriber sendCompleted];
return nil;
}] filter:^BOOL(id value) {
return (_downloadFileOperation);
}];
}
但是:我的下载文件操作没有停止。当我在 VC
中使用时
[[_downloadMapFileOperation signalForPauseDownloadMap]
subscribeNext:^(id x) {
[downloadMapView setTitleForStartDownloadButton:@"Resume download"];
_currentDownloadState = kHTDownloadStatePaused;
}];
我的进度值工作正常(不会从一个值跳到另一个值)。请帮帮我。谢谢。
对于您关于跳跃下载进度的原始问题,问题是您正在创建对信号的两个副本的订阅,这导致了两次下载。即,这段代码:
[[[DownloadMapFileOperation sharedInstance] signalForDownloadMap:self.place]
subscribeNext:^(AFHTTPRequestOperation *operation) {
NSString *alertMessage = @"Download complete";
[[[UIAlertView alloc] initWithTitle:@"Complete" message:alertMessage delegate:self cancelButtonTitle:@"Close" otherButtonTitles: nil] show];
downloadMapView.progressLabel.text = @"Download Complete";
}];
[[[DownloadMapFileOperation sharedInstance] signalForDownloadMap:self.place] subscribeError:^(NSError *error) {
[[[UIAlertView alloc] initWithTitle:@"Error" message:[error localizedDescription] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil] show];
}];
每个 subscribeNext
/subscribeError
/等等。将创建一个新的订阅,并开始下载。您要订阅的内容如下:
[[[DownloadMapFileOperation sharedInstance] signalForDownloadMap:self.place]
subscribeNext:^(AFHTTPRequestOperation *operation) {
NSString *alertMessage = @"Download complete";
[[[UIAlertView alloc] initWithTitle:@"Complete" message:alertMessage delegate:self cancelButtonTitle:@"Close" otherButtonTitles: nil] show];
downloadMapView.progressLabel.text = @"Download Complete";
} error:^(NSError *error) {
[[[UIAlertView alloc] initWithTitle:@"Error" message:[error localizedDescription] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil] show];
}];
对于暂停和恢复,我认为创建更多信号没有意义。如果你确实有权访问下载文件操作,那么直接调用pause/resume就可以了。原始订阅将继续有效,并且应该 pause/resume 根据创建的信号内的行为进行更新。
至于取消,你真正想做的是使用一次性用品。在[RACSignal createSignal:]
调用中,你不想returnnil
。相反,设置一个一次性用品,在一次性用品被处理后取消下载。
编辑:
理想情况下,您希望通过信号发送进度。在 setDownloadProgressBlock
你会改为:
NSMutableDictionary *progressDictionary = [[NSMutableDictionary alloc] init];
progressDictionary[@"current"] = @(totalBytesRead);
progressDictionary[@"total"] = @(totalBytesExpectedToRead);
progressDictionary[@"progress"] = @((float)totalBytesRead / (float)totalBytesExpectedToRead);
[subscriber sendNext:[progressDictionary copy]];
然后您可以在订阅时阅读 progressDictionary,如下所示:
[[[DownloadMapFileOperation sharedInstance] signalForDownloadMap:self.place]
subscribeNext:^(NSDictionary *progressDictionary) {
// Do stuff with dictionary here, like update UI with progress
} error:^(NSError *error) {
// Error related stuff
} completed:^{
// Completed stuff
}];
注意:如果没有充分的理由,您不应该通过 sendNext 发送 nil。完成逻辑应该只在完成块中完成。
我正在使用反应式 cocoa 从服务器创建下载文件。我有一个 DownloadMapFileOperation.m 文件,进度为 属性(进度值、currentBytes、totalBytes)。它在我的下一个 RACSignal
方法和浮动进度中变化非常快(这是不必要的,但是
@property (nonatomic, strong, readwrite) NSMutableDictionary *progressDictionary;
@property (nonatomic, assign, readwrite) float progress;
和RACSignal
创建方法
- (RACSignal *)signalForDownloadMap:(Place *)place
{
return [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSString *mapUrlString = [NSString stringWithFormat:@"http://myurl/%@", place.mapFileName];
NSURL *url = [NSURL URLWithString:mapUrlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
_downloadFileOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:[url lastPathComponent]];
[_downloadFileOperation setOutputStream:[NSOutputStream outputStreamToFileAtPath:fullPath append:NO]];
[_downloadFileOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation , id response)
{
NSLog(@"Downloaded via compl block");
[subscriber sendNext:nil];
[subscriber sendCompleted];
} failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
[subscriber sendError:error];
}];
@weakify(self)
self.progressDictionary = [[NSMutableDictionary alloc] init];
self.progressDictionary[@"progress"] = @(0);
self.progressDictionary[@"current"] = @(0);
self.progressDictionary[@"total"] = @(0);
self.progress = @(0);
[_downloadFileOperation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead)
{
@strongify(self)
self.progressDictionary[@"current"] = @(totalBytesRead);
self.progressDictionary[@"total"] = @(totalBytesExpectedToRead);
self.progressDictionary[@"progress"] = @((float)totalBytesRead / (float)totalBytesExpectedToRead);
self.progress = @((float)totalBytesRead / (float)totalBytesExpectedToRead);
self.progress = @((float)totalBytesRead / (float)totalBytesExpectedToRead);
}];
[_downloadFileOperation start];
return nil;
}] filter:^BOOL(id value) {
return !(.mapFileName == nil);
}]
deliverOn:[RACScheduler mainThreadScheduler]
];
}
我只是想通过 RACObserve
在我的 ViewController 中显示我的进度。
[[[DownloadMapFileOperation sharedInstance] signalForDownloadMap:self.place]
subscribeNext:^(AFHTTPRequestOperation *operation) {
NSString *alertMessage = @"Download complete";
[[[UIAlertView alloc] initWithTitle:@"Complete" message:alertMessage delegate:self cancelButtonTitle:@"Close" otherButtonTitles: nil] show];
downloadMapView.progressLabel.text = @"Download Complete";
}];
[[[DownloadMapFileOperation sharedInstance] signalForDownloadMap:self.place] subscribeError:^(NSError *error) {
[[[UIAlertView alloc] initWithTitle:@"Error" message:[error localizedDescription] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil] show];
}];
RAC(downloadMapView, progressView.progress) = [RACObserve([DownloadMapFileOperation sharedInstance], progress)
map:^id(id value) {
return value;
}];
RAC(downloadMapView, progressLabel.text) = [RACObserve([DownloadMapFileOperation sharedInstance], progressDictionary)
map:^id(NSDictionary *value) {
return [value[@"progress"] isEqual: @(1)] ? @"Download Complete" : [NSString stringWithFormat:@"Downloaded %@ of %@", value[@"current"], value[@"total"]];
}];
但我的进度视图有时会从 60% 跳到 50%(或 45 - 35 等),并且 NSLog(@"Downloaded via compl block");
在进度达到 100% 之前显示(有时达到并从大约 70% 开始)。
如果我使用 progressDictionary
(就像第二个 RACObserve
)- 它不起作用(显示 0 进度和下载 0,共 0)
已编辑
我也尝试在我的 DownloadMapFileOperation.m 文件中添加暂停、恢复、取消下载操作的方法。
- (RACSignal *)signalForPauseDownloadMap
{
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[_downloadFileOperation pause];
[subscriber sendNext:nil];
[subscriber sendCompleted];
return nil;
}];
}
- (RACSignal *)signalForResumeDownloadMap
{
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[_downloadFileOperation resume];
[subscriber sendNext:nil];
[subscriber sendCompleted];
return nil;
}];
}
- (RACSignal *)signalForCancelDownloadMap
{
return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[_downloadFileOperation cancel];
[subscriber sendNext:nil];
[subscriber sendCompleted];
return nil;
}] filter:^BOOL(id value) {
return (_downloadFileOperation);
}];
}
但是:我的下载文件操作没有停止。当我在 VC
中使用时[[_downloadMapFileOperation signalForPauseDownloadMap]
subscribeNext:^(id x) {
[downloadMapView setTitleForStartDownloadButton:@"Resume download"];
_currentDownloadState = kHTDownloadStatePaused;
}];
我的进度值工作正常(不会从一个值跳到另一个值)。请帮帮我。谢谢。
对于您关于跳跃下载进度的原始问题,问题是您正在创建对信号的两个副本的订阅,这导致了两次下载。即,这段代码:
[[[DownloadMapFileOperation sharedInstance] signalForDownloadMap:self.place]
subscribeNext:^(AFHTTPRequestOperation *operation) {
NSString *alertMessage = @"Download complete";
[[[UIAlertView alloc] initWithTitle:@"Complete" message:alertMessage delegate:self cancelButtonTitle:@"Close" otherButtonTitles: nil] show];
downloadMapView.progressLabel.text = @"Download Complete";
}];
[[[DownloadMapFileOperation sharedInstance] signalForDownloadMap:self.place] subscribeError:^(NSError *error) {
[[[UIAlertView alloc] initWithTitle:@"Error" message:[error localizedDescription] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil] show];
}];
每个 subscribeNext
/subscribeError
/等等。将创建一个新的订阅,并开始下载。您要订阅的内容如下:
[[[DownloadMapFileOperation sharedInstance] signalForDownloadMap:self.place]
subscribeNext:^(AFHTTPRequestOperation *operation) {
NSString *alertMessage = @"Download complete";
[[[UIAlertView alloc] initWithTitle:@"Complete" message:alertMessage delegate:self cancelButtonTitle:@"Close" otherButtonTitles: nil] show];
downloadMapView.progressLabel.text = @"Download Complete";
} error:^(NSError *error) {
[[[UIAlertView alloc] initWithTitle:@"Error" message:[error localizedDescription] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil] show];
}];
对于暂停和恢复,我认为创建更多信号没有意义。如果你确实有权访问下载文件操作,那么直接调用pause/resume就可以了。原始订阅将继续有效,并且应该 pause/resume 根据创建的信号内的行为进行更新。
至于取消,你真正想做的是使用一次性用品。在[RACSignal createSignal:]
调用中,你不想returnnil
。相反,设置一个一次性用品,在一次性用品被处理后取消下载。
编辑:
理想情况下,您希望通过信号发送进度。在 setDownloadProgressBlock
你会改为:
NSMutableDictionary *progressDictionary = [[NSMutableDictionary alloc] init];
progressDictionary[@"current"] = @(totalBytesRead);
progressDictionary[@"total"] = @(totalBytesExpectedToRead);
progressDictionary[@"progress"] = @((float)totalBytesRead / (float)totalBytesExpectedToRead);
[subscriber sendNext:[progressDictionary copy]];
然后您可以在订阅时阅读 progressDictionary,如下所示:
[[[DownloadMapFileOperation sharedInstance] signalForDownloadMap:self.place]
subscribeNext:^(NSDictionary *progressDictionary) {
// Do stuff with dictionary here, like update UI with progress
} error:^(NSError *error) {
// Error related stuff
} completed:^{
// Completed stuff
}];
注意:如果没有充分的理由,您不应该通过 sendNext 发送 nil。完成逻辑应该只在完成块中完成。