Bug using simulator 4.1 and not 4.0


#1

Hi,
I fond a bug in the ‘Solutions 13. Homepwner’ code:

using the iPhone simulator 4.1 I get this error.

[Session started at 2010-09-27 11:56:49 -0400.]
2010-09-27 11:57:03.934 Homepwner[3715:207] Using two-stage rotation animation. To use the smoother single-stage animation, this application must remove two-stage method implementations.
2010-09-27 11:57:03.935 Homepwner[3715:207] Using two-stage rotation animation is not supported when rotating more than one view controller or view controllers not the window delegate
2010-09-27 11:57:06.988 Homepwner[3715:207] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[__NSCFDictionary setObject:forKey:]: attempt to insert nil value (key: 55032314-7D89-4A08-9C10-A443BFBC700F)’
*** Call stack at first throw:
(
0 CoreFoundation 0x02495b99 __exceptionPreprocess + 185
1 libobjc.A.dylib 0x025e540e objc_exception_throw + 47
2 CoreFoundation 0x0244e238 +[NSException raise:format:arguments:] + 136
3 CoreFoundation 0x0244e1aa +[NSException raise:format:] + 58
4 CoreFoundation 0x02494405 -[__NSCFDictionary setObject:forKey:] + 293
5 Homepwner 0x00004301 -[ImageCache setImage:forKey:] + 68
6 Homepwner 0x0000395b -[ItemDetailViewController imagePickerController:didFinishPickingMediaWithInfo:] + 387
7 UIKit 0x004973c0 -[UIImagePickerController _imagePickerDidCompleteWithInfo:] + 139
8 PhotoLibrary 0x0c4b5a3d PLNotifyImagePickerOfImageAvailability + 1937
9 PhotoLibrary 0x0c4cb5f6 -[PLUIAlbumViewController albumView:didTapPhotoAtIndex:] + 429
10 PhotoLibrary 0x0c51bc1d -[PLAlbumView _tapGesture:] + 349
11 UIKit 0x00542060 -[UIGestureRecognizer _updateGestureWithEvent:] + 727
12 UIKit 0x0053e8bf -[UIGestureRecognizer _delayedUpdateGesture] + 47
13 UIKit 0x00543152 _UIGestureRecognizerUpdateObserver + 637
14 UIKit 0x00544464 _UIGestureRecognizerUpdateGesturesFromSendEvent + 51
15 UIKit 0x002ed844 -[UIWindow _sendGesturesForEvent:] + 1292
16 UIKit 0x002e93bf -[UIWindow sendEvent:] + 105
17 UIKit 0x002cccb4 -[UIApplication sendEvent:] + 447
18 UIKit 0x002d19bf _UIApplicationHandleEvent + 7672
19 GraphicsServices 0x02d75822 PurpleEventCallback + 1550
20 CoreFoundation 0x02476ff4 CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION + 52
21 CoreFoundation 0x023d7807 __CFRunLoopDoSource1 + 215
22 CoreFoundation 0x023d4a93 __CFRunLoopRun + 979
23 CoreFoundation 0x023d4350 CFRunLoopRunSpecific + 208
24 CoreFoundation 0x023d4271 CFRunLoopRunInMode + 97
25 GraphicsServices 0x02d7400c GSEventRunModal + 217
26 GraphicsServices 0x02d740d1 GSEventRun + 115
27 UIKit 0x002d5af2 UIApplicationMain + 1160
28 Homepwner 0x00001d34 main + 102
29 Homepwner 0x00001cc5 start + 53
)
terminate called after throwing an instance of ‘NSException’

= = = = = = = =
using the iPhone simulator 4.0 you only get the warning that has been talked about:

[Session started at 2010-09-27 11:58:15 -0400.]
2010-09-27 11:58:20.647 Homepwner[3746:207] Using two-stage rotation animation. To use the smoother single-stage animation, this application must remove two-stage method implementations.
2010-09-27 11:58:20.648 Homepwner[3746:207] Using two-stage rotation animation is not supported when rotating more than one view controller or view controllers not the window delegate

= = = = = = = =

Thanks

  • oldrocker

#2

Hm, this is coming from adding a nil pointer to an NSDictionary.

In ItemDetailViewController.m, in the imagePickerController:didFinishPickingMedia: method, there is a line of code:


	UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];

Make sure that’s okay by following this line with

NSLog(@"%@ %@", info, image);

To make sure it is non-nil at least at this point.


#3

In case it helps, I got a similar error due to misspelling a method name in the ImageCache.h file, specifically “shareImageCache” instead of “sharedImageCache.” I didn’t notice the warning message the first time around. That probably isn’t the exact fix but I remembered getting something similar so I figured I would share. Hope that helps. Take care.


#4

OK,
I just want to be clear, this is YOUR sample code from the Solutions download.
I thought you might want to know Apple breaks your code with 4.1.

Looking at this… Huh?
UIImagePickerControllerOriginalImage is now UIImagePickerControllerReferenceURL??
Too new at this to know how to debug this.

iPhone simulator 4.0
NSLog(@"%@ %@", info, image);
= = = = = = = =
2010-09-29 09:40:02.665 Homepwner[19419:207] {
UIImagePickerControllerMediaType = “public.image”;
UIImagePickerControllerOriginalImage = “<UIImage: 0x5926210>”;
} <UIImage: 0x5926210>

iPhone simulator 4.1
NSLog(@"%@ %@", info, image);
= = = = = = = =
2010-09-29 09:38:55.715 Homepwner[19398:207] {
UIImagePickerControllerMediaType = “public.image”;
UIImagePickerControllerReferenceURL = “assets-library://asset/asset.jpg?id=113&ext=jpg”;
} (null)

FULL DUMP:
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

iPhone simulator 4.0 with NSLog(@"%@ %@", info, image);

[Session started at 2010-09-29 09:39:46 -0400.]
2010-09-29 09:39:59.929 Homepwner[19419:207] Using two-stage rotation animation. To use the smoother single-stage animation, this application must remove two-stage method implementations.
2010-09-29 09:39:59.930 Homepwner[19419:207] Using two-stage rotation animation is not supported when rotating more than one view controller or view controllers not the window delegate
2010-09-29 09:40:02.665 Homepwner[19419:207] {
UIImagePickerControllerMediaType = “public.image”;
UIImagePickerControllerOriginalImage = “<UIImage: 0x5926210>”;
} <UIImage: 0x5926210>

iPhone simulator 4.1 with NSLog(@"%@ %@", info, image);

[Session started at 2010-09-29 09:38:39 -0400.]
2010-09-29 09:38:53.195 Homepwner[19398:207] Using two-stage rotation animation. To use the smoother single-stage animation, this application must remove two-stage method implementations.
2010-09-29 09:38:53.196 Homepwner[19398:207] Using two-stage rotation animation is not supported when rotating more than one view controller or view controllers not the window delegate
2010-09-29 09:38:55.715 Homepwner[19398:207] {
UIImagePickerControllerMediaType = “public.image”;
UIImagePickerControllerReferenceURL = “assets-library://asset/asset.jpg?id=113&ext=jpg”;
} (null)
2010-09-29 09:38:55.806 Homepwner[19398:207] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[__NSCFDictionary setObject:forKey:]: attempt to insert nil value (key: 93247D1D-855A-41FD-9DA0-3D1FB284660A)’
*** Call stack at first throw:
(
0 CoreFoundation 0x02495b99 __exceptionPreprocess + 185
1 libobjc.A.dylib 0x025e540e objc_exception_throw + 47
2 CoreFoundation 0x0244e238 +[NSException raise:format:arguments:] + 136
3 CoreFoundation 0x0244e1aa +[NSException raise:format:] + 58
4 CoreFoundation 0x02494405 -[__NSCFDictionary setObject:forKey:] + 293
5 Homepwner 0x000042f1 -[ImageCache setImage:forKey:] + 68
6 Homepwner 0x0000394b -[ItemDetailViewController imagePickerController:didFinishPickingMediaWithInfo:] + 415
7 UIKit 0x004973c0 -[UIImagePickerController _imagePickerDidCompleteWithInfo:] + 139
8 PhotoLibrary 0x0c4b5a3d PLNotifyImagePickerOfImageAvailability + 1937
9 PhotoLibrary 0x0c4cb5f6 -[PLUIAlbumViewController albumView:didTapPhotoAtIndex:] + 429
10 PhotoLibrary 0x0c51bc1d -[PLAlbumView _tapGesture:] + 349
11 UIKit 0x00542060 -[UIGestureRecognizer _updateGestureWithEvent:] + 727
12 UIKit 0x0053e8bf -[UIGestureRecognizer _delayedUpdateGesture] + 47
13 UIKit 0x00543152 _UIGestureRecognizerUpdateObserver + 637
14 UIKit 0x00544464 _UIGestureRecognizerUpdateGesturesFromSendEvent + 51
15 UIKit 0x002ed844 -[UIWindow _sendGesturesForEvent:] + 1292
16 UIKit 0x002e93bf -[UIWindow sendEvent:] + 105
17 UIKit 0x002cccb4 -[UIApplication sendEvent:] + 447
18 UIKit 0x002d19bf _UIApplicationHandleEvent + 7672
19 GraphicsServices 0x02d75822 PurpleEventCallback + 1550
20 CoreFoundation 0x02476ff4 CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION + 52
21 CoreFoundation 0x023d7807 __CFRunLoopDoSource1 + 215
22 CoreFoundation 0x023d4a93 __CFRunLoopRun + 979
23 CoreFoundation 0x023d4350 CFRunLoopRunSpecific + 208
24 CoreFoundation 0x023d4271 CFRunLoopRunInMode + 97
25 GraphicsServices 0x02d7400c GSEventRunModal + 217
26 GraphicsServices 0x02d740d1 GSEventRun + 115
27 UIKit 0x002d5af2 UIApplicationMain + 1160
28 Homepwner 0x00001d08 main + 102
29 Homepwner 0x00001c99 start + 53
)
terminate called after throwing an instance of ‘NSException’


#5

It looks like the simulator is giving back a reference URL for images picked from the library. That’s interesting. I’ll investigate further.

If you build to the device, you should see the correct behavior.


#6

I just built the solution provided on the website against 4.1, and I see no difference in the behavior from 4.0.

How big is the image you are selecting?

Perhaps post your ItemDetailViewController.m file?


#7

tried it with a smaller image… worked,
It is interesting that 4.1 on the simulator and iPhone both blow up on larger images.

Simulator 4.1
2010-09-29 16:58:51.715 Homepwner[508:207] {
UIImagePickerControllerMediaType = “public.image”;
UIImagePickerControllerOriginalImage = “<UIImage: 0x5f7c370>”;
UIImagePickerControllerReferenceURL = “assets-library://asset/asset.JPG?id=1000000001&ext=JPG”;
} <UIImage: 0x5f7c370>

Simulator 4.0

2010-09-29 17:00:12.373 Homepwner[530:207] {
UIImagePickerControllerMediaType = “public.image”;
UIImagePickerControllerOriginalImage = “<UIImage: 0x595a180>”;
} <UIImage: 0x595a180>

= = = = = = =
Here is the ItemDetailViewController.m from the Solutions download

  • NSLog(@"%@ %@", info, image);
    = = = = = = =

//
// ItemDetailViewController.m
// Homepwner
//
// Created by bhardy on 7/30/09.
// Copyright 2009 Big Nerd Ranch. All rights reserved.
//

#import “ItemDetailViewController.h”
#import “Possession.h”
#import “ImageCache.h”
#import <MobileCoreServices/MobileCoreServices.h>
#import <MediaPlayer/MediaPlayer.h>

@implementation ItemDetailViewController

  • (id)init
    {
    [super initWithNibName:@"ItemDetailViewController"
    bundle:nil];

    // Create a UIBarButtonItem with a camera icon, will send
    // takePicture: to our ItemDetailViewController when tapped
    UIBarButtonItem *cameraBarButtonItem =
    [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCamera
    target:self
    action:@selector(takePicture:)];

    // Place this image on our navigation bar when this viewcontroller
    // is on top of the navigation stack
    [[self navigationItem] setRightBarButtonItem:cameraBarButtonItem];

    // cameraBarButton is retained by the navigation item
    [cameraBarButtonItem release];

    return self;
    }

  • (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)bundle
    {
    return [self init];
    }

  • (void)viewDidLoad
    {
    [[self view] setBackgroundColor:[UIColor groupTableViewBackgroundColor]];
    [self setEditingPossession:editingPossession];
    }

  • (void)takePicture:(id)sender
    {
    [[self view] endEditing:YES];

    UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];

    // If our device has a camera, we want to take a picture, otherwise, we just
    // pick from photo library
    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
    [imagePicker setSourceType:UIImagePickerControllerSourceTypeCamera];
    else
    [imagePicker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary];

    // image picker needs a delegate so we can respond to its messages
    [imagePicker setDelegate:self];

    // Place image picker on the screen – NEW
    if([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
    if(imagePickerPopover)
    [imagePickerPopover release];
    imagePickerPopover = [[UIPopoverController alloc]
    initWithContentViewController:imagePicker];
    [imagePickerPopover setDelegate:self];
    [imagePickerPopover presentPopoverFromBarButtonItem:sender
    permittedArrowDirections:UIPopoverArrowDirectionAny
    animated:YES];
    } else {
    [self presentModalViewController:imagePicker animated:YES];
    }
    // The image picker will be retained until it has been dismissed
    [imagePicker release];
    }
    // NEW

  • (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController
    {
    [imagePickerPopover release];
    imagePickerPopover = nil;
    }

  • (void)imagePickerController:(UIImagePickerController *)picker
    didFinishPickingMediaWithInfo:(NSDictionary *)info
    {
    NSString *oldKey = [editingPossession imageKey];

    // Did the possession already have an image?
    if (oldKey) {

      // Delete the old image
      [[ImageCache sharedImageCache] deleteImageForKey:oldKey];
    

    }

    // Get picked image from info dictionary
    UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
    NSLog(@"%@ %@", info, image);

    // Create a CFUUID object - it knows how to create unique identifiers
    CFUUIDRef newUniqueID = CFUUIDCreate (kCFAllocatorDefault);

    // Create a string from unique identifier
    CFStringRef newUniqueIDString = CFUUIDCreateString (kCFAllocatorDefault, newUniqueID);

    // Use that unique ID to set our possessions imageKey
    [editingPossession setImageKey:(NSString *)newUniqueIDString];

    // We used “Create” in the functions to generate objects, we need to release them
    CFRelease(newUniqueIDString);
    CFRelease(newUniqueID);

    // Store image in the ImageCache with this key
    [[ImageCache sharedImageCache] setImage:image forKey:[editingPossession imageKey]];

    // Put that image on to the screen in our image view
    [imageView setImage:image];

    // Take image picker off the screen
    [self dismissModalViewControllerAnimated:YES];
    // NEW
    [imagePickerPopover dismissPopoverAnimated:YES];
    }

  • (void)setEditingPossession:(Possession *)possession
    {
    // Keep a pointer to the incoming possession
    editingPossession = possession;
    }

  • (void)viewWillAppear:(BOOL)animated
    {
    // Use properties of incoming possession to change user interface
    [nameField setText:[editingPossession possessionName]];
    [serialNumberField setText:[editingPossession serialNumber]];
    [valueField setText:[NSString stringWithFormat:@"%d",
    [editingPossession valueInDollars]]];

    // Create a NSDateFormatter… we filter NSDate objects through this formatted
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateStyle:NSDateFormatterMediumStyle];
    [dateFormatter setTimeStyle:NSDateFormatterNoStyle];

    // Use filtered NSDate object to return string to set dateLabel contents
    [dateLabel setText:
    [dateFormatter stringFromDate:[editingPossession dateCreated]]];
    [dateFormatter release];

    // Change the nav item to display name of possession
    [[self navigationItem] setTitle:[editingPossession possessionName]];
    NSString *imageKey = [editingPossession imageKey];

    if (imageKey) {
    // Get image for image key from image cache
    UIImage *imageToDisplay = [[ImageCache sharedImageCache] imageForKey];

      // Use that image to put on the screen in imageView
      [imageView setImage:imageToDisplay];
    

    } else {
    // Clear the imageView
    [imageView setImage:nil];
    }
    }

  • (void)viewWillDisappear:(BOOL)animated
    {
    [super viewWillDisappear];

    [nameField resignFirstResponder];
    [serialNumberField resignFirstResponder];
    [valueField resignFirstResponder];

    // “Save” changes to editingPossession
    [editingPossession setPossessionName:[nameField text]];
    [editingPossession setSerialNumber:[serialNumberField text]];
    [editingPossession setValueInDollars:[[valueField text] intValue]];
    }

  • (BOOL)textFieldShouldReturn:(UITextField *)textField
    {
    [textField resignFirstResponder];
    return YES;
    }

  • (void)didReceiveMemoryWarning {
    // Releases the view if it doesn’t have a superview.
    [super didReceiveMemoryWarning];

    // Release any cached data, images, etc that aren’t in use.
    }

  • (void)viewDidUnload {
    [super viewDidUnload];

    [nameField release];
    nameField = nil;

    [serialNumberField release];
    serialNumberField = nil;

    [valueField release];
    valueField = nil;

    [dateLabel release];
    dateLabel = nil;

    [imageView release];
    imageView = nil;
    }

  • (void)dealloc
    {
    [imageView release];
    [nameField release];
    [serialNumberField release];
    [valueField release];
    [dateLabel release];
    [super dealloc];
    }

@end


#8

Yeah, there has supposedly always been a restriction on UIImage objects that are greater than 1024x1024 except for in some circumstances. It looks like the UIImagePickerController is being a bit more safe about what images it actually loads into memory.

Images coming back from the camera seem to always make it back in the info dictionary - but this could be because they are smaller than some threshold (that your image was crossing). I do wonder (and will test when I get a chance) where that reference URL points to when pulling an image from the photo library that is too large. My guess is that it copies the image into your tmp directory, but I couldn’t say for sure.

Here is the Apple docs on UIImage about size of images:
“You should avoid creating UIImage objects that are greater than 1024 x 1024 in size. Besides the large amount of memory such an image would consume, you may run into problems when using the image as a texture in OpenGL ES or when drawing the image to a view or layer. This size restriction does not apply if you are performing code-based manipulations, such as resizing an image larger than 1024 x 1024 pixels by drawing it to a bitmap-backed graphics context. In fact, you may need to resize an image in this manner (or break it into several smaller images) in order to draw it to one of your views.”