我一直在研究NSProgress,但发现现有的文档、类引用和教程都是缺乏的。我主要想知道我的NSProgress是否适用于我的用例。类参考文档也是指suboperations或subtasks,我可能弄错了,但我将suboperations解释为是指NSOperation管理一组其他NSOperations的情况。我的用例示例如下:
Upload All Items in Group操作。NSOperationQueue。Upload All Items in Group操作将为其组中的每个项创建一个Upload Item操作。这些都会添加到由操作管理的NSOperationQueue中。我希望NSProgress能够支持这一点,并允许我将进度从嵌套操作(Upload Item操作)传播到父操作,最后传播到主线程和UI。但我很难实现这一点,似乎NSProgress更适合在一个后台线程上执行所有代码的长时间操作,但是有单独的“部分”,便于确定何时取得进展,如果是这样的话,那么使用suboperation这个术语有点误导人,因为它会让人想起嵌套NSOperations的使用。
谢谢你能提供的任何帮助,如果需要更多的细节,请告诉我。
发布于 2013-10-06 21:01:01
NSProgress对NSOperations一无所知--这两件事是正交的--但这并不意味着它不能和它们一起使用。嵌套NSProgress“任务”的思想是,内部任务不了解外部任务,外部任务不需要直接访问内部任务的NSProgress来获取更新。我编造了一个小例子:
// Outer grouping
NSProgress* DownloadGroupsOfFiles(NSUInteger numGroups, NSUInteger filesPerGroup)
{
// This is the top level NSProgress object
NSProgress* p = [NSProgress progressWithTotalUnitCount: numGroups];
for (NSUInteger i = 0; i < numGroups; ++i)
{
// Whatever DownloadFiles does, it's worth "1 unit" to us.
[p becomeCurrentWithPendingUnitCount: 1];
DownloadFiles(filesPerGroup);
[p resignCurrent];
}
return p;
}
// Inner grouping
void DownloadFiles(NSUInteger numberOfFiles)
{
NSProgress* p = [NSProgress progressWithTotalUnitCount: numberOfFiles];
NSOperationQueue* opQueue = [[NSOperationQueue alloc] init];
// Make the op queue last as long as the NSProgress
objc_setAssociatedObject(p, NULL, opQueue, OBJC_ASSOCIATION_RETAIN);
// For each file...
for (NSUInteger i = 0; i < numberOfFiles; ++i)
{
// Whatever this DownloadOperation does is worth 1 "unit" to us.
[p becomeCurrentWithPendingUnitCount: 1];
// Make the new operation
MyDownloadOperation* op = [[MyDownloadOperation alloc] initWithName: [NSString stringWithFormat: @"File #%@", @(i+1)]];
[opQueue addOperation: op];
[p resignCurrent];
}
}
// And then the DownloadOperation might look like this...
@interface MyDownloadOperation : NSOperation
@property (nonatomic, readonly, copy) NSString* name;
- (id)initWithName: (NSString*)name;
@end
@implementation MyDownloadOperation
{
NSProgress* _progress;
NSString* _name;
}
- (id)initWithName:(NSString *)name
{
if (self = [super init])
{
_name = [name copy];
// Do this in init, so that our NSProgress instance is parented to the current one in the thread that created the operation
_progress = [NSProgress progressWithTotalUnitCount: 1];
}
return self;
}
- (void)dealloc
{
_name = nil;
_progress = nil;
}
- (void)main
{
// Fake like we're doing something that takes some time
// Determine fake size -- call it 768K +- 256K
const NSUInteger size = 512 * 1024 + arc4random_uniform(512*1024);
const NSUInteger avgBytesPerSec = 1024 * 1024;
const NSTimeInterval updatePeriod = 1.0/60.0;
// Make sure all the updates to the NSProgress happen on the main thread
// in case someone is bound to it.
dispatch_async(dispatch_get_main_queue(), ^{
_progress.totalUnitCount = size;
_progress.completedUnitCount = 0;
});
NSUInteger bytesRxd = 0;
do
{
// Sleep for a bit...
usleep(USEC_PER_SEC * updatePeriod);
// "Receive some data"
NSUInteger rxdThisTime = updatePeriod * avgBytesPerSec;
// Never report more than all the bytes
bytesRxd = MIN(bytesRxd + rxdThisTime, size);
// Update on the main thread...
dispatch_async(dispatch_get_main_queue(), ^{
[_progress setCompletedUnitCount: bytesRxd];
});
} while (bytesRxd < size);
}
@end需要注意的一点是,如果使用NSProgress向UI传递状态,那么您将希望确保每次更新NSProgress对象时都要从主线程进行更新,否则会发生很多奇怪的崩溃。
另外,您也可以使用NSURLConnection下载文件,然后使用如下所示的委托:
@interface MyURLConnectionProgressReporter : NSObject <NSURLConnectionDownloadDelegate>
@property (nonatomic, readwrite, assign) id<NSURLConnectionDownloadDelegate> delegate;
@end
NSProgress* DownloadABunchOfFiles(NSArray* arrayOfURLs)
{
arrayOfURLs = arrayOfURLs.count ? arrayOfURLs : @[ [NSURL URLWithString: @"http://www.google.com"] ];
NSProgress* p = [NSProgress progressWithTotalUnitCount: arrayOfURLs.count];
for (NSURL* url in arrayOfURLs)
{
[p becomeCurrentWithPendingUnitCount: 1];
MyURLConnectionProgressReporter* delegate = [[MyURLConnectionProgressReporter alloc] init];
NSURLConnection* conn = [[NSURLConnection alloc] initWithRequest: [NSURLRequest requestWithURL: url] delegate: delegate];
[conn start];
[p resignCurrent];
}
return p;
}
@implementation MyURLConnectionProgressReporter
{
NSProgress* _progress;
}
static void EnsureMainThread(dispatch_block_t block);
- (id)init
{
if (self = [super init])
{
_progress = [NSProgress progressWithTotalUnitCount: 1];
EnsureMainThread(^{
_progress.kind = NSProgressKindFile;
[_progress setUserInfoObject:NSProgressFileOperationKindDownloading forKey:NSProgressFileOperationKindKey];
});
}
return self;
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
id retVal = [super forwardingTargetForSelector:aSelector];
if (!retVal && [self.delegate respondsToSelector: _cmd])
{
retVal = self.delegate;
}
return retVal;
}
- (void)p_updateWithTotalBytesWritten:(long long)totalBytesWritten expectedTotalBytes:(long long) expectedTotalBytes
{
// Update our progress on the main thread...
EnsureMainThread(^{
if (!expectedTotalBytes)
_progress.totalUnitCount = -1;
else
_progress.totalUnitCount = MAX(_progress.totalUnitCount, expectedTotalBytes);
_progress.completedUnitCount = totalBytesWritten;
});
}
- (void)connection:(NSURLConnection *)connection didWriteData:(long long)bytesWritten totalBytesWritten:(long long)totalBytesWritten expectedTotalBytes:(long long) expectedTotalBytes
{
// Update our progress
[self p_updateWithTotalBytesWritten: totalBytesWritten expectedTotalBytes: expectedTotalBytes];
// Then call on through to the other delegate
if ([self.delegate respondsToSelector: _cmd])
{
[self.delegate connection:connection didWriteData:bytesWritten totalBytesWritten:totalBytesWritten expectedTotalBytes:expectedTotalBytes];
}
}
- (void)connectionDidResumeDownloading:(NSURLConnection *)connection totalBytesWritten:(long long)totalBytesWritten expectedTotalBytes:(long long) expectedTotalBytes
{
// Update our progress
[self p_updateWithTotalBytesWritten: totalBytesWritten expectedTotalBytes: expectedTotalBytes];
// Then call on through to the other delegate
if ([self.delegate respondsToSelector: _cmd])
{
[self.delegate connectionDidResumeDownloading:connection totalBytesWritten:totalBytesWritten expectedTotalBytes:expectedTotalBytes];
}
}
- (void)connectionDidFinishDownloading:(NSURLConnection *)connection destinationURL:(NSURL *) destinationURL
{
// We're done, so we want (_progress.completedUnitCount == _progress.totalUnitCount)
EnsureMainThread(^{
_progress.completedUnitCount = _progress.totalUnitCount;
});
if ([self.delegate respondsToSelector: _cmd])
{
[self.delegate connectionDidFinishDownloading:connection destinationURL:destinationURL];
}
}
static void EnsureMainThread(dispatch_block_t block)
{
if (!block)
return;
else if ([NSThread isMainThread])
block();
else
dispatch_async(dispatch_get_main_queue(), block);
}
@end希望这能有所帮助。
https://stackoverflow.com/questions/19188476
复制相似问题