【问题标题】:NSProgress fractionCompleted key-value observing method not calledNSProgress fractionCompleted 键值观察方法未调用
【发布时间】:2014-03-07 17:01:11
【问题描述】:

我正在尝试使用 AFNetworking 和 NSProgress 跟踪我的 HTTP 请求的进度。基本上,我的请求是一个包含文本参数和图像数据的多部分表单数据。

这是我的上传任务代码,对我来说似乎很简单和正常:

NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"<THE_API_URL>" parameters:nil constructingBodyWithBlock:constructionBlock];

AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];

NSProgress *progress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
[progress addObserver:self forKeyPath:@"fractionCompleted" options:NSKeyValueObservingOptionNew context:NULL];
NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithStreamedRequest:request progress:&progress completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
    if (error) {
        // UI reacts to error
    } else {
        // Do stuff here
    }
}];
[uploadTask resume];

现在,我不明白的是,即使上传任务完成并成功,回调观察者方法也没有被调用。有人可以帮我理解为什么吗?这是我的代码。

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];

    if ([keyPath isEqualToString:@"fractionCompleted"] && [object isKindOfClass:[NSProgress class]]) {
        NSProgress *progress = (NSProgress *)object;
        NSLog(@"Progress is: %f", progress.fractionCompleted);
    }
}

更新:我遵循 AFNetworking 的 uploadTaskWithStreamedRequest:... 方法代码,发现它们实际上是从 uploadTaskWithTask:... 内部替换我的 NSProgress 对象。自然,如果NSProgress 指针指向一个新实例,则该新实例不会注册以观察fractionCompleted 属性。如何在不直接修改 AFNetworking 代码的情况下对这个新的 NSProgress 实例进行键值观察?

【问题讨论】:

  • fractionCompleted 是只读的,所以我怀疑它是在 completedUnitCount 更改时计算的......所有这些都意味着 fractionCompleted 可能是不可观察的。也许如果你尝试观察 completedUnitCount 呢?
  • 恐怕它是可观察到的,如 NSProgress 文档中所述:Each of the properties of a progress object, including totalUnitCount, completedUnitCount, and fractionCompleted, support Key-Value Observing.
  • @merlevede 同意。我调查了AFURLSessionManagerTaskDelegate 来源。代码仅更改 completedUnitCount 属性。
  • 我按照 AFNetworking 的 uploadTaskWithStreamedRequest:... 方法代码,发现它们实际上是从 uploadTaskWithTask:... 内部替换我的 NSProgress 对象。自然,如果 NSProgress 指针指向一个新实例,则该新实例不会注册以观察fractionCompleted 属性。如何在不直接修改 AFNetworking 代码的情况下对这个新的 NSProgress 实例进行键值观察?
  • 您的问题实际上帮助我以不同的方式看待问题并解决了我一直坚持一段时间的问题:完全覆盖 UIProgressView 可以正常使用 NSProgress(又名 .observedProgress)。你的问题让我想起了关于 NSProgress 的 WWDC 2015 会议以及 WWDC 面包屑和你的问题,bam,我有 real 循环进度视图,现在可以基于简单地更新 completedUnitCount aka 来进行各种绘图和动画theProgress.completedUnitCount += 1; 代码中的其他地方。如果可以的话,我会为你投 10 次赞成票。

标签: ios iphone objective-c ipad cocoa-touch


【解决方案1】:

我放弃了这种跟踪AFURLSessionManager 进度的方法。相反,我使用了一个很好的旧AFHTTPRequestOperation

NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"THE_API_URL" parameters:nil constructingBodyWithBlock:constructionBlock];

AFHTTPRequestOperation *requestOperation = [[AFHTTPRequestOperationManager manager] HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
    // Do stuff here
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    // Catch error
}];
[requestOperation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
    double percentDone = (double)totalBytesWritten / (double)totalBytesExpectedToWrite;
    progressBlock(percentDone);
}];
[requestOperation start];

【讨论】:

  • 谢谢朋友,我无法设置 af2 方法有效。 kvo和直接使用af的进度查看方法都没有响应,所以我和你一样切换到af1方式,我的问题已经解决了。
  • 如果您在管理器上运行多个下载,这会导致问题。 KVO 是监控单个下载进度的正确方法
【解决方案2】:

您需要做的就是在创建上传任务后将您的观察者移到那里,它会像魅力一样工作。

NSProgress *progress;

NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithStreamedRequest:request progress:&progress completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
    if (error) {
        // UI reacts to error
    } else {
        // Do stuff here
    }
}];
[uploadTask resume];

[progress addObserver:self forKeyPath:@"fractionCompleted" options:NSKeyValueObservingOptionNew context:NULL];

【讨论】:

  • 这是正确答案。 uploadTaskWithStreamedRequest:创建一个新的进度对象并返回它。如果您在此调用之前设置 KVO,它将被丢弃
【解决方案3】:

你可能会遇到这个问题,https://github.com/AFNetworking/AFNetworking/issues/1398,这实际上是由于 NSURLSession 中的设计限制。在讨论即将结束时,您将看到一个建议的解决方法。我正在使用这种解决方法,并且观察到“fractionCompleted”对我来说效果很好。

-布莱恩

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-02-04
    • 1970-01-01
    • 2011-03-29
    • 2018-07-22
    • 2016-03-04
    • 1970-01-01
    • 2022-01-13
    相关资源
    最近更新 更多