我正在使用Xcode 4.6.3和iOS 5.5 / 6.1.6。
我正在使用后台线程从服务器向iOS设备加载大量的jpg。
dispatch_async(kBgQueue, ^
{
// get the array of filenames to download
NSURL* url = [NSURL URLWithString:webPath];
NSArray* theArray = [NSArray arrayWithContentsOfURL:url];
if( theArray )
{
dispatch_async(dispatch_get_main_queue(), ^{
// disable screen buttons
[self setButtons:false];
});
[self loadImagesFromList:theArray sourceBundle:bundlePath destBundle:localBundlePath manager:manager];
if (!stopFlag) {
// if no memory error has occurred
NSLog(@"calling refresh after load_images");
dispatch_async(dispatch_get_main_queue(), ^{
[self refresh];
});
}
theArray = nil;
}
else
{
NSLog(@"Error loading bundle");
}
});
背景方法:
-(void)loadImagesFromList:(NSArray *)theArray
sourceBundle:(NSString *)bundlePath
destBundle:(NSString *)localBundlePath
manager:(NSFileManager *)manager {
// initialize the progress and activity indicator
dispatch_async(dispatch_get_main_queue(), ^{
[self.activityIndictor startAnimating];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[self.progressIndictor setProgress:0 animated:NO];
});
NSURL *url;
NSString *srcFile;
NSString *destFile;
NSError *error = nil;
int counter = 0;
float prog = 0;
float increment = 1.0 / [theArray count];
float stepSize = [theArray count] / 10;
for (NSString *file in theArray)
{
if (stopFlag) {
NSLog(@"I see stopFlag = true, counter = %d, prog = %f", counter, prog);
return;
}
srcFile = [bundlePath stringByAppendingPathComponent:file];
destFile = [localBundlePath stringByAppendingPathComponent:file];
counter += 1;
prog += increment;
if (counter == stepSize) {
dispatch_async(dispatch_get_main_queue(), ^{
self.progressIndictor.progress = prog;
});
counter = 0;
}
// only download if file isn't already here
BOOL fileExists = [manager fileExistsAtPath:destFile]; // check if we already have it
if (!fileExists) {
// jpg or folder check
if ([[destFile pathExtension] isEqualToString:@"jpg"]) {
url = [NSURL URLWithString:srcFile];
data = [NSData dataWithContentsOfURL:url
options:0
error:&error];
[data writeToFile:destFile options:NSDataWritingAtomic error:&error];
data = nil;
} else {
[manager createDirectoryAtPath:destFile withIntermediateDirectories:YES attributes:nil error:&error];
}
}
}
}
如果文件存在,循环将拉开数组并退回到主线程ok。 如果缺少任何文件,下载/写入部分似乎会咀嚼RAM并导致低内存警告触发。这需要几千个文件。
我尝试在循环外声明变量,甚至在主线程中执行整个操作以测试是否导致泄漏。 我尝试使用备用dataWithContentsOfURL:options:错误调用。 我试过了仪器,但它确实很慢并经常崩溃。在崩溃之前,它确实显示分配上升,上升,缓慢上升。
经过几天的努力,我很难过。
答案 0 :(得分:7)
我建议的第一件事就是使用@autoreleasepool
来控制消耗的峰值内存量。现在,您将内容作为自动释放对象下载到NSData
中,完成后,您将nil
- 变量loadImagesFromList
- 该变量简单标记为一次解除分配排出自动释放池(在for
完成之前不会发生这种情况)。通过(a)在@autoreleasepool
循环内移动变量声明; (b)将其包装在-(void)loadImagesFromList:(NSArray *)theArray
sourceBundle:(NSString *)bundlePath
destBundle:(NSString *)localBundlePath
manager:(NSFileManager *)manager {
// initialize the progress and activity indicator
dispatch_async(dispatch_get_main_queue(), ^{
// your UI update here
});
int counter = 0;
float prog = 0;
float increment = 1.0 / [theArray count];
float stepSize = [theArray count] / 10;
for (NSString *file in theArray)
{
@autoreleasepool {
if (stopFlag) {
NSLog(@"I see stopFlag = true, counter = %d, prog = %f", counter, prog);
return;
}
NSString *srcFile = [bundlePath stringByAppendingPathComponent:file];
NSString *destFile = [localBundlePath stringByAppendingPathComponent:file];
counter += 1;
prog += increment;
if (counter == stepSize) {
dispatch_async(dispatch_get_main_queue(), ^{
self.progressIndictor.progress = prog;
});
counter = 0;
}
// only download if file isn't already here
BOOL fileExists = [manager fileExistsAtPath:destFile]; // check if we already have it
if (!fileExists) {
NSError *error = nil;
// jpg or folder check
if ([[destFile pathExtension] isEqualToString:@"jpg"]) {
NSURL *url = [NSURL URLWithString:srcFile];
NSData *data = [NSData dataWithContentsOfURL:url
options:0
error:&error];
[data writeToFile:destFile options:NSDataWritingAtomic error:&error];
} else {
[manager createDirectoryAtPath:destFile withIntermediateDirectories:YES attributes:nil error:&error];
}
}
}
}
}
中,当个人下载完成后,您的记忆将被解除分配。
{{1}}
答案 1 :(得分:0)
您可能希望重构此代码以使用NSOperationQueue
。这解决了峰值内存问题,但也让您享受一定程度的并发性。因为iOS无论如何只允许4-5个并发请求,所以您希望将最大并发操作数限制为合理的数量,如果尝试运行过多的并发请求,这可以减轻网络超时风险。 (此maxConcurrentOperationCount
功能是我建议使用操作队列的主要原因。)
无论如何,这可能看起来像:
-(void)loadImagesFromList:(NSArray *)theArray
sourceBundle:(NSString *)bundlePath
destBundle:(NSString *)localBundlePath
manager:(NSFileManager *)manager {
// initialize the progress and activity indicator
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// your UI update here
}];
int __block counter = 0;
float __block prog = 0;
float increment = 1.0 / [theArray count];
float stepSize = [theArray count] / 10;
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 4;
for (NSString *file in theArray)
{
[queue addOperationWithBlock:^{
if (stopFlag) {
NSLog(@"I see stopFlag = true, counter = %d, prog = %f", counter, prog);
return;
}
NSString *srcFile = [bundlePath stringByAppendingPathComponent:file];
NSString *destFile = [localBundlePath stringByAppendingPathComponent:file];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
counter += 1;
prog += increment;
if (counter == stepSize) {
self.progressIndictor.progress = prog;
counter = 0;
}
}];
// only download if file isn't already here
BOOL fileExists = [manager fileExistsAtPath:destFile]; // check if we already have it
if (!fileExists) {
NSError *error = nil;
// jpg or folder check
if ([[destFile pathExtension] isEqualToString:@"jpg"]) {
NSURL *url = [NSURL URLWithString:srcFile];
NSData *data = [NSData dataWithContentsOfURL:url
options:0
error:&error];
[data writeToFile:destFile options:NSDataWritingAtomic error:&error];
} else {
[manager createDirectoryAtPath:destFile withIntermediateDirectories:YES attributes:nil error:&error];
}
}
}];
}
}
我可能会建议其他一些改进(例如,实现取消逻辑而不是查看stopFlag
),但我试图最小化代码更改。我只是利用了一个可以轻松替换dispatch_async
的事实:
dispatch_async(dispatchQueue, ^{ ... });
使用NSOperationQueue
方法addOperationWithBlock
:
[operationQueue addOperationWithBlock:^{ ... }];
但是现在我们可以使用并发NSOperationQueue
和maxConcurrentOperationCount
的4或5,你会突然享受一个很好的,受限制的并发度。您可能会发现这比按顺序下载文件要快得多。