La aplicación funciona bien en 4S, pero falla en 3G, debido a SIGABRT

When the user starts the app for the first time he gets a pop up and has to save an image. It works on the simulator and on the 4S. But when I start it with my 3G it gives me a SIGABRT error as soon as I chose a picture. I assume its because of the size of the picture which is too pick and therefore claiming all of the ram - But that is rather weird, because I make it much smaller. Here is the code:

- (void)viewDidLoad
{ 
    [super viewDidLoad];
    if ([[NSUserDefaults standardUserDefaults] objectForKey:@"bild"] == nil) { 
        picker = [[UIImagePickerController alloc] init];
        picker.delegate = self;
        picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
        [self presentModalViewController:picker animated:YES];
    }
}

- (void)imagePickerController:(UIImagePickerController *) Picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
    double compressionRatio=1;
    NSData *imageData=UIImageJPEGRepresentation([info objectForKey:@"bild"],compressionRatio);
    while ([imageData length]>5000) { 
        compressionRatio=compressionRatio*0.20;
        imageData=UIImageJPEGRepresentation([info objectForKey:@"bild"],compressionRatio);
    }

    UIImage *yourUIImage;
    yourUIImage = [info objectForKey:UIImagePickerControllerOriginalImage];
    imageData = [NSKeyedArchiver archivedDataWithRootObject:yourUIImage];
    [[NSUserDefaults standardUserDefaults] setObject:imageData forKey:@"bild"];
    [[NSUserDefaults standardUserDefaults] synchronize];

    [self dismissModalViewControllerAnimated:YES];
}

I get the SIGABRT error at this line imageData = [NSKeyedArchiver archivedDataWithRootObject:yourUIImage];

Should I use another method to resize the image? Or should I save it as a local file and retrieve it everytime the app starts? (If its possible)

preguntado el 31 de enero de 12 a las 08:01

3 Respuestas

Está utilizando NSCoding to archive an UIImage into a NSData object.
Before iOS5 UIImage didn't implement the methods for NSCoding. You simply can't use archivedDataWithRootObject: with UIImages before iOS5.
That's why you get an exception on the iPhone 3G.

This is just a very educated guess because I can't confirm that with documentation or a test on a device but there are a lot of questions and forum posts around that ask how to implement NSCoding on UIImage.


and this whole block of code isn't called at all because [info objectForKey:@"bild"] regresará nil. The info dictionary does not contain an object for the key bild. Documentation for UIImagePickerControllerDelegate_Protocol contiene una List of valid keys

NSData *imageData=UIImageJPEGRepresentation([info objectForKey:@"bild"],compressionRatio);
while ([imageData length]>5000) { 
    compressionRatio=compressionRatio*0.20;
    imageData=UIImageJPEGRepresentation([info objectForKey:@"bild"],compressionRatio);
}

you probably want to use something like that:

NSData *data;
if ([[UIImage class] conformsToProtocol:@protocol(NSCoding)]) {
    // >= iOS5
    data = [NSKeyedArchiver archivedDataWithRootObject:image];
    // save so you know it's an archived UIImage object
}
else {
    // < iOS5
    data = /* your UIImageJPEGRepresentation method but this time with the key UIImagePickerControllerOriginalImage instead of "bild" */
    // save so you know it's raw JPEG data
}

Respondido el 31 de enero de 12 a las 13:01

Thanks for your answer. I tried it like this, but it doesnt seem to work atm. He doesnt dismiss the picker. But that maybe, because I might have implemented it wrong? - Espada

Edit: Another error is probably that you're looking for an object for the key "bild" within the info dictionary you're passed from the image picker controller. It will have no such key. You should pass the key UIImagePickerControllerOriginalImage to get at the image instead.

--

You're not throwing away the old imageData. If your problem is high memory consumption, you'll have to do away with the image data as you progressively shrink the image.

Additionally, an image may be too big anyway to fit within 5000 bytes. Your current code doesn't handle this gracefully. Eventually, the compression ratio will grow beyond 1.0 at which point the function will probably throw an exception.

I'd suggest resizing the image before trying to compress it heavily.

No matter how you make the image data smaller, your current code keeps all the old copies around until the next time the autorelease pool is drained. Since the data returned from UIImageJPEGRepresentation is autoreleased, you may try using an inline autorelease pool around every loop iteration to drain the data immediately.

Respondido el 31 de enero de 12 a las 12:01

Hey, thanks for your quick response. I'm using ARC. So how can I use drain the data immediately as autorelease, release and so on do not work with ARC? - Espada

Change the while loop to a do-while loop and change imageData to a block local variable in @autoreleasepool { ... }. When you've found something you intend to keep, assign it to the real imageData. - Jesper

If the SIGABRT is consistently happening at imageData = [NSKeyedArchiver archivedDataWithRootObject:yourUIImage]; then my feeling would be that it is likely to be that specific line, rather than a memory issue.

UIImage never used to conform to NSCoding, but the current docs say that it does. It could be that the 3G, which will be limited to iOS 4.2.1 is using a non-conformant version and so crashes when you try to archive, whereas the 4S on iOS 5 uses the new conformant version.

Try adding a category for NSCoding to UIImage and then rerun on the 3G. Example at http://iphonedevelopment.blogspot.com/2009/03/uiimage-and-nscoding.html

Respondido el 31 de enero de 12 a las 13:01

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.