0

I want to copy picture from the photo library to another directory in my app, every thing works just fine with the attached code here but when I try to copy a lot of images the app just crashes immediately I think that because the attached code is done with a single thread so each image needs its thread, so if there is too many pictures to copy the app crashes. I need the same code here but not in a thread that means the app should be blocked until the image is copied to the another directory , if any one have another good idea I would be appreciated. the for loop is saving each image.

for (int i = 0; i< countt ;i++) {

        NSURL *referenceURL = [self.ToSaveArray objectAtIndex:i];
        ALAssetsLibrary *assetLibrary=[[ALAssetsLibrary alloc] init];
        [assetLibrary assetForURL:referenceURL resultBlock:^(ALAsset *asset) {
            ALAssetRepresentation *rep = [asset defaultRepresentation];
            Byte *buffer = (Byte*)malloc(rep.size);
            NSUInteger buffered = [rep getBytes:buffer fromOffset:0.0 length:rep.size error:nil];
            NSData *data = [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];//this is NSData may be what you want
            NSLog(@"length %d",[data length]);
            UIImage *image= [UIImage imageWithData:data];
            [self SavePhoto:image withnum:i];
            //[data writeToFile:photoFile atomically:YES];//you can save image later
        } failureBlock:^(NSError *err) {
            NSLog(@"Error: %@",[err localizedDescription]);
        }];

    }

save photo code:

-(bool)SavePhoto:(UIImage *) imageTosave withnum:(int)num{


    float goodScal = imageTosave.size.width/75.0;

    CGSize newSize =CGSizeMake(imageTosave.size.width/goodScal, imageTosave.size.height/goodScal);
    UIImage* smallImage = [self resizeImage:imageTosave toSize:newSize];
    NSData *JpgDataS = UIImageJPEGRepresentation(smallImage, 1);
    NSData *JpgData = UIImageJPEGRepresentation(imageTosave, 1);



    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsPath = [paths objectAtIndex:0]; //Get the docs directory
    NSString *dataPath = [documentsPath stringByAppendingPathComponent:@"/CapturesPhotos"];
    NSDate *date = [NSDate date];
    NSDateFormatter *formatter = [[[NSDateFormatter alloc] init]autorelease];
    NSTimeZone *zone = [NSTimeZone localTimeZone];
    [formatter setTimeZone:zone];
    [formatter setDateFormat:@"yyyyMMddHHmmss"];
    NSString* Bnamee = [NSString stringWithFormat:@"/IMG%d_%@B.jpg",num,[formatter stringFromDate:date]];
    NSString* Snamee = [NSString stringWithFormat:@"/IMG%d_%@S.jpg",num,[formatter stringFromDate:date]];
    //NSString *filePath = [dataPath stringByAppendingPathComponent:namee]; //Add the file name
    NSString *filePath = [dataPath stringByAppendingPathComponent:Bnamee]; //Add the file name
    NSString *filePathS = [dataPath stringByAppendingPathComponent:Snamee]; //Add the file name
    [JpgData writeToFile:filePath atomically:YES]; //Write the file
    [JpgDataS writeToFile:filePathS atomically:YES]; //Write the file
    //[pngData writeToFile:filePath atomically:YES]; //Write the file
    return true;
}

thanks in advance

flashdisk
  • 3,652
  • 3
  • 16
  • 22
  • What about the implementation of SavePhoto? – David Berry Apr 23 '14 at 19:37
  • I have edited my question with the code. – flashdisk Apr 23 '14 at 19:40
  • What's showing up in the log when the app terminates? – David Berry Apr 23 '14 at 19:42
  • For an approach to determining when multiple blocks have finished executing, see my answer [here](http://stackoverflow.com/questions/23253175/how-to-tell-if-blocks-in-loop-all-have-completed-executing/23253323#23253323) – David Berry Apr 23 '14 at 19:49
  • I get: received memory warning! – flashdisk Apr 23 '14 at 19:49
  • Ok, that confirms that it's an issue with running out of memory, probably because of trying to have all the images in memory simultaneously. Try creating a serial dispatch queue and queueing blocks to do the assetForUrl: block, or maybe better queue a block inside the assetForUrl block to do the allocation, fetch, and save. – David Berry Apr 23 '14 at 19:52
  • Can you give me example of that I am not familiar with the dispatch queues! – flashdisk Apr 23 '14 at 19:55
  • Edited my answer to demonstrate using a dispatch queue to serialize memory intensive operations. – David Berry Apr 23 '14 at 20:10

1 Answers1

1

It seems likely that starting off that many asynchronous operations is running you out of memory, try serializing the expensive (memory-wise) parts of it using a serial queue:

dispatch_queue_t    queue = dispatch_queue_create("save", DISPATCH_QUEUE_SERIAL);

// Move this out of loop for efficiency
ALAssetsLibrary*    assetLibrary=[[ALAssetsLibrary alloc] init];

for (int i = 0; i< ToSaveArray.count ;i++) {

    NSURL *referenceURL = [ToSaveArray objectAtIndex:i];
    [assetLibrary assetForURL:referenceURL resultBlock:^(ALAsset *asset) {
        ALAssetRepresentation *rep = [asset defaultRepresentation];
        dispatch_async(queue, ^{

            Byte *buffer = (Byte*)malloc(rep.size);
            NSUInteger buffered = [rep getBytes:buffer fromOffset:0.0 length:rep.size error:nil];
            NSData *data = [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];//this is NSData may be what you want
            NSLog(@"length %d",[data length]);
            UIImage *image= [UIImage imageWithData:data];
            [self SavePhoto:image withnum:i];
        });
        //[data writeToFile:photoFile atomically:YES];//you can save image later
    } failureBlock:^(NSError *err) {
        NSLog(@"Error: %@",[err localizedDescription]);
    }];

}

dispatch_async(queue, ^{
    // Some code to execute when all the saving is done
});

I'm not sure if this will actually solve the problem, but it's worth trying :)

Also, a corrected version of how to get the UIImage more cleanly (from this answer):

UIImage*            image = [UIImage imageWithCGImage:[rep fullResolutionImage]
                                                scale:[rep scale]
                                          orientation:UIImageOrientationUp];

Although in your case it's probably better to continue fetching the image data itself, just pass that into your save routine so you can save the expense of converting jpg->image->jpg. You still need to create the image so you can also save the thumbnail, but that's another matter.

Community
  • 1
  • 1
David Berry
  • 40,941
  • 12
  • 84
  • 95
  • no,that's not correct your answer did not solve the problem, the thread are working concurrently that is the problem and the answer you have provided also flips the image! – flashdisk Apr 23 '14 at 19:33
  • you should pay attention to : freeWhenDone:YES which means that the buffer is freed when done using it. – flashdisk Apr 23 '14 at 19:35
  • freeWhenDone:YES please pay attention to this!! – flashdisk Apr 23 '14 at 19:36
  • Technically, I think my code unflips it. iOS kind of cheats and doesn't actually rotate the photo from the camera, instead it just changes the jpg image orientation, CGImage loses the jpeg image orientation information, and the image unflips. – David Berry Apr 23 '14 at 19:59