Я столкнулся с большой проблемой. То, что я пытаюсь сделать, это получить данные с сервера, но сервер одним нажатием дает все данные, которые очень велики по количеству, и я выполняю весь свой процесс в основном потоке, поэтому в нем около 400-500 изображений. форма URL, которую я сохраняю в каталоге документов в виде NSData. Итак, в навигаторе dubug, когда потребление памяти достигло 80-90 МБ, мое приложение вылетело и показало следующую ошибку: -
mach_vm_map(size=135168) failed (error code=3)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
2015-01-23 17:10:03.946 ArchaioMobileViewer[853:148470] *** Terminating app due to uncaught exception 'NSMallocException', reason: 'Attempt to allocate 262144 bytes for NS/CFData failed'
Я использую ARC, но проблема с памятью все еще возникает. Это мой код `-
(void)downloadDocumentsFromServer:(NSDictionary *)documentsList IsUpdate:(BOOL)isUpdate;
{
//Main Target(22)
BusinessLayer* bLL = [[BusinessLayer alloc]init];
FileManager* downloadImages = [FileManager alloc];
for(NSDictionary* inspDocumentResult in documentsList)
{
FloorDocument* floorDocument = [[FloorDocument alloc]init];
floorDocument.docID = [inspDocumentResult objectForKey:@"docID"];
floorDocument.buildingID = selectedBuildingID;
floorDocument.clientID = clientID;
NSDictionary* documentArray = [inspDocumentResult objectForKey:@"Document"];
floorDocument.docType = [[documentArray objectForKey:@"Type"] stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
floorDocument.docScale = [documentArray objectForKey:@"Scale"];
floorDocument.docDescription = [documentArray objectForKey:@"DocDesc"];
//floorDocument.floor = [bLL getFloorNameByDocIDAndBuildingID:selectedBuildingID DocID:floorDocument.docID];
floorDocument.floor = [inspDocumentResult objectForKey:@"Floor"];
NSLog(@"%@",[inspDocumentResult objectForKey:@"hiResImage"]);
[downloadImages downloadInspectionDocuments:[inspDocumentResult objectForKey:@"hiResImage"] ImageName:floorDocument.docID FileType:floorDocument.docType Folder:selectedBuildingID];
NSLog(@"Floor %@ - High Res Image copied for %@",floorDocument.floor,floorDocument.docID);
//Download the Low Res Image
NSString* lowResImage = [inspDocumentResult objectForKey:@"lowResImage"];
[downloadImages downloadInspectionDocumentsLowRes:lowResImage ImageName:floorDocument.docID FileType:floorDocument.docType Folder:selectedBuildingID LowResName:@"lowResImage"];
//Copy the Quarter Size File
lowResImage = [lowResImage stringByReplacingOccurrencesOfString:@"LowRes" withString:@"LowRes4"];
[downloadImages downloadInspectionDocumentsLowRes:lowResImage ImageName:floorDocument.docID FileType:floorDocument.docType Folder:selectedBuildingID LowResName:@"lowResImage4"];
NSLog(@"Floor %@ - Low Res Images copied for %@",floorDocument.floor,floorDocument.docID);
//Download the tiles
NSArray* tiles = [inspDocumentResult objectForKey:@"lsUrls"];
for(NSString* tile in tiles)
{
@autoreleasepool {
NSArray* tileNameArray = [tile componentsSeparatedByString:@"/"];
if(tileNameArray.count > 0)
{
NSString* destTile = [tileNameArray objectAtIndex:tileNameArray.count-1];
destTile = [destTile stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@".%@",floorDocument.docType] withString:@""];
NSLog(@"TileName:%@",destTile);
[downloadImages downloadInspectionDocumentsTiles:tile ImageName:floorDocument.docID FileType:floorDocument.docType Folder:selectedBuildingID TileName:destTile];
}
}
}
NSLog(@"Floor %@ - Tiles Image copied for %@",floorDocument.floor,floorDocument.docID);
NSLog(@"Downloading Documents Tiles For %@ Completed at %@",floorDocument.docID,[bLL getCurrentDate]);
[bLL saveFloorDocuments:floorDocument IsUpdate:isUpdate];
// downloadImages=nil;
}
bLL = nil;
} пожалуйста, помогите мне в этой проблеме.`
Это код, который я использую внутри DownloadInspectionDocuments: -
-(void)downloadInspectionDocuments:(NSString *)url ImageName:(NSString *)imageName FileType:(NSString*)fileType Folder:(NSString*)folder
{
@autoreleasepool
{
NSString* source =[FileManager getInspectionDocumentsFolder];
//Lets get the destination folder
NSString *destination = [NSString stringWithFormat:@"%@/%@/%@",source,folder,imageName];
[self createFolder:destination CreateSubFolders:true];
NSString *filePath = [NSString stringWithFormat:@"%@/%@.%@",destination,imageName,fileType];
NSFileManager* fm = [[NSFileManager alloc]init];
if(![fm fileExistsAtPath:filePath])
{
NSData *data1 = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
[data1 writeToFile:filePath atomically:YES];
}
}
// return [NSString stringWithFormat:@"%@.%@",imageName,fileType];
}
2 ответа
ARC не является сборщиком мусора: он вставляет код управления памятью (сохраняет/освобождает) за вас, но вам все равно нужно убедиться, что вы не используете слишком много ресурсов (так же, как в коде без ARC).
Вы запускаете этот большой цикл в основном потоке, поэтому любая потребляемая память не будет освобождена до следующего цикла выполнения.
Вам нужно разбить эту функцию на более мелкие шаги, которые можно выполнять поэтапно.
На данный момент, если для одной итерации внешнего цикла функции не используется слишком много памяти, вы можете добавить пул автоматического освобождения на этом уровне (я вижу, что вы включили его во внутреннем цикле)
for(NSDictionary* inspDocumentResult in documentsList)
{
@autoreleasepool {
.... remaining code goes here
}
}
И он, по крайней мере, будет сливать то, что может, на каждой итерации.
Учитывая, что вы загружаете большое количество файлов и будете полагаться на сетевое подключение, я бы рекомендовал выполнять загрузку асинхронно. Если вы еще этого не сделали, ознакомьтесь с AFNetworking, чтобы упростить это. Это даст вам гораздо больший контроль над вашими ресурсами, чем вы получаете сейчас с ресурсоемким блокирующим вызовом в основном потоке.
Вы можете сэкономить много работы, следуя советам Давбрина и Андреа по использованию AFNetworking и потоковой передачи файла. По сути, не помещайте весь файл в память, а затем записывайте его на диск, записывайте на диск по мере получения байтов из сети. Это должно снизить нагрузку на память. Например:
- (void)downloadFile:(NSString *)urlString {
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];
NSString *destinationPath = [NSDocumentDirectory() stringByAppendingPathComponent:@"some-file-name"];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setOutputStream:[NSOutputStream outputStreamToFileAtPath:destinationPath append:NO]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(@"Super duper awesome!");
// Maybe start another download here?
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"Error downloading file: %@", error);
}];
[operation start];
}
Итак, все, что вам нужно сделать, это создать список вещей для загрузки, и в вашем блоке успеха начать загрузку другого файла.
Похожие вопросы
Связанные вопросы
Новые вопросы
ios
iOS - мобильная операционная система, работающая на Apple iPhone, iPod touch и iPad. Используйте этот тег [ios] для вопросов, связанных с программированием на платформе iOS. Используйте связанные теги [target-c] и [swift] для проблем, характерных для этих языков программирования.
downloadInspectionDocuments