【问题标题】:UIImagePickerControllerDelegate on iPad on iOS4 crashes (works fine in iOS5)iOS4 上 iPad 上的 UIImagePickerControllerDelegate 崩溃(在 iOS5 中运行良好)
【发布时间】:2012-02-19 16:01:49
【问题描述】:

我有一个问题,在我选择照片后访问照片库会触发崩溃。

日志输出为:

[GEPhotoControllerPopOver respondsToSelector:]: message sent to deallocated instance 0x239fbf40

崩溃发生在没有调用堆栈的 Main 中。如果我使用 Guard Malloc 运行,我只会在日志中收到上述错误。

GEPhotoControllerPopOver 是我的照片库弹出框。似乎有些东西在被释放后试图访问它,但对于我的生活,我不知道是什么。我在每段调用 GEPhotoControllerPopover 的代码中都设置了断点,并且在 GEPhotoControllerPopover 释放后没有调用它。

它是这样声明的:

@interface GEPhotoControllerPopOver : UIViewController <    UINavigationControllerDelegate, 
                                                        UIImagePickerControllerDelegate,
                                                        UIPopoverControllerDelegate
                                                    > 
{
    char* m_pPixelData;
    int m_photoWidth;
    int m_photoHeight;
    int m_bytesPerPixel;

    GEClient *m_pClient;    
    int m_longEdge;

    UIImage* m_pLevelFrame;
    UIImageView* m_pLevelFrameView;

    UIPopoverController *m_pPopoverController;
}

- (void)UseImage:(UIImage*)theImage:(UIImagePickerController *)picker;
@end

从图片库中获取图片的代码是:

extern UIView *g_glView;

@implementation GEPhotoControllerPopOver


- (id)init
{
    return [super init];
}

- (void)loadView
{   
    [super loadView];
}

- (void)dealloc
{
    [super dealloc];
}

- (void)SelectPhoto
{
    if( [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary ] )
    {
        self.view = g_glView;

        UIImagePickerController *pImagePicker;

        pImagePicker = [[UIImagePickerController alloc] init];

        pImagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;

        pImagePicker.delegate = self;

        pImagePicker.mediaTypes = [NSArray arrayWithObjects:(NSString *) kUTTypeImage, nil];

        pImagePicker.allowsEditing = NO;

        m_pPopoverController = [[UIPopoverController alloc] initWithContentViewController:pImagePicker];
        m_pPopoverController.delegate = self;

        [m_pPopoverController setPopoverContentSize:CGSizeMake(160,160) animated:YES];
        CGRect selectedRect = CGRectMake(0,0,1,1);
        [m_pPopoverController presentPopoverFromRect:selectedRect inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];

        [pImagePicker release];
    }
}

-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    [m_pPopoverController dismissPopoverAnimated:true];

    [m_pPopoverController release];
    m_pPopoverController = nil;

    NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];

    [self dismissModalViewControllerAnimated:YES];

    if ([mediaType isEqualToString:(NSString *)kUTTypeImage]) {
        UIImage *picture = [info objectForKey:UIImagePickerControllerOriginalImage];

        [self UseImage:picture:picker];     
    }
}

我不认为我做的事情太疯狂了。我将图像数据传递给 UseImage 中的其余代码,在那里我复制了它,所以我没有指向同一个内存。

然后我解除分配 GEPhotoControllerPopOver 并且不再调用它。不幸的是,几帧之后发生了上述错误。仅在 iOS4.3 上的 iPad 上。它在 iPhone(不同的界面对象)和带有 iOS3.x 和 iOS5.x 的 iPad 上运行良好

有人有什么想法吗?

谢谢!

编辑 - 似乎删除对 [super dealloc] 的调用会使问题消失。显然不是最好的解决方案,但可能有助于弄清楚发生了什么? 我还逐步检查了我的所有代码,试图找出崩溃的地方,但无济于事。它关闭弹出框,经历一个完整的渲染周期,完成绘制周期,然后在程序集深处崩溃。

编辑 2 - 以下代码在 iOS4 上不再崩溃,但会泄漏 32k 和 128k 的内存。

32K 泄漏:

0 libsystem_c.dylib calloc
1 MusicLibrary MemNewPtrClear
2 MusicLibrary ReadITImageDB
3 MusicLibrary -[MLPhotoLibrary _loadImageLibrary]
4 MusicLibrary -[MLPhotoLibrary albums]
5 PhotoLibrary -[PLPhotoLibrary albums]
6 PhotoLibrary -[PLPhotoLibrary imagePickerAlbums]
7 PhotoLibrary -[PLPhotoLibrary(Utilities) albumsForContentMode:]
8 PhotoLibrary -[PLLibraryViewController _updateAlbumsIfNecessary]
9 PhotoLibrary -[PLLibraryViewController viewWillAppear:]
10 PhotoLibrary -[PLUILibraryViewController viewWillAppear:]
11 UIKit -[UINavigationController _startTransition:fromViewController:toViewController:]
12 UIKit -[UINavigationController _startDeferredTransitionIfNeeded]
13 UIKit -[UILayoutContainerView layoutSubviews]
14 QuartzCore -[CALayer layoutSublayers]
15 QuartzCore CALayerLayoutIfNeeded
16 QuartzCore CA::Context::commit_transaction(CA::Transaction*)
17 QuartzCore CA::Transaction::commit()
18 QuartzCore CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long,  void*)
19 CoreFoundation __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
20 CoreFoundation __CFRunLoopDoObservers
21 CoreFoundation __CFRunLoopRun
22 CoreFoundation CFRunLoopRunSpecific
23 CoreFoundation CFRunLoopRunInMode
24 GraphicsServices GSEventRunModal
25 GraphicsServices GSEventRun
26 UIKit UIApplicationMain

128K 泄漏:

0 libsystem_c.dylib malloc
1 MusicLibrary ReadITImageDB
2 MusicLibrary -[MLPhotoLibrary _loadImageLibrary]
3 MusicLibrary -[MLPhotoLibrary albums]
4 PhotoLibrary -[PLPhotoLibrary albums]
5 PhotoLibrary -[PLPhotoLibrary imagePickerAlbums]
6 PhotoLibrary -[PLPhotoLibrary(Utilities) albumsForContentMode:]
7 PhotoLibrary -[PLLibraryViewController _updateAlbumsIfNecessary]
8 PhotoLibrary -[PLLibraryViewController viewWillAppear:]
9 PhotoLibrary -[PLUILibraryViewController viewWillAppear:]
10 UIKit -[UINavigationController _startTransition:fromViewController:toViewController:]
11 UIKit -[UINavigationController _startDeferredTransitionIfNeeded]
12 UIKit -[UILayoutContainerView layoutSubviews]
13 QuartzCore -[CALayer layoutSublayers]
14 QuartzCore CALayerLayoutIfNeeded
15 QuartzCore CA::Context::commit_transaction(CA::Transaction*)
16 QuartzCore CA::Transaction::commit()
17 QuartzCore CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*)
18 CoreFoundation __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
19 CoreFoundation __CFRunLoopDoObservers
20 CoreFoundation __CFRunLoopRun
21 CoreFoundation CFRunLoopRunSpecific
22 CoreFoundation CFRunLoopRunInMode
23 GraphicsServices GSEventRunModal
24 GraphicsServices GSEventRun
25 UIKit UIApplicationMain

新代码:

-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    [m_pPopoverController dismissPopoverAnimated:true];

    [m_pPopoverController release];
    m_pPopoverController = nil;

    NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];

    [self dismissModalViewControllerAnimated:YES];

    if ([mediaType isEqualToString:(NSString *)kUTTypeImage]) {
        UIImage *picture = [info objectForKey:UIImagePickerControllerOriginalImage];

        [self UseImage:picture:picker];     
    }

    [picker dismissModalViewControllerAnimated:YES];

//  [picker release]; // <- CAUSES CRASH on BOTH iOS4 and iOS5.
}


- (void)SelectPhoto
{
    if( [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary ] )
    {
        self.view = g_glView;

        UIImagePickerController *pImagePicker;

        pImagePicker = [[UIImagePickerController alloc] init];

        pImagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;

        pImagePicker.delegate = self;

        pImagePicker.mediaTypes = [NSArray arrayWithObjects:(NSString *) kUTTypeImage, nil];

        pImagePicker.allowsEditing = NO;

        m_pPopoverController = [[UIPopoverController alloc] initWithContentViewController:pImagePicker];
        m_pPopoverController.delegate = self;

        [m_pPopoverController setPopoverContentSize:CGSizeMake(160,160) animated:YES];
        CGRect selectedRect = CGRectMake(0,0,1,1);
        [m_pPopoverController presentPopoverFromRect:selectedRect inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];

        [pImagePicker release];
    }
 }

这是我的 UseImage 代码:

- (void)UseImage:(UIImage*)theImage:(UIImagePickerController *)picker
{
    CGFloat width, height;

    // if image came from camera, save it to photo library
    if( picker.sourceType == UIImagePickerControllerSourceTypeCamera )
    {
        UIImageWriteToSavedPhotosAlbum(theImage, nil, nil, nil);    
    }

    CGImageRef imageRef = [theImage CGImage];
    width = CGImageGetWidth(imageRef);
    height = CGImageGetHeight(imageRef);    

    size_t bitsPerPixel = CGImageGetBitsPerPixel( imageRef );

    m_photoWidth = width;
    m_photoHeight = height;
    m_bytesPerPixel = bitsPerPixel / 8;


    CGContextRef cgctx = CreateARGBBitmapContextPopOver(theImage.CGImage, picker.sourceType, m_pClient);
    if (cgctx == NULL) 
    { 
        // error creating context
        return;
    }

    // Get image width, height. We'll use the entire image.
    size_t w = CGImageGetWidth(theImage.CGImage);
    size_t h = CGImageGetHeight(theImage.CGImage);
    CGRect rect = {{0,0},{w,h}}; 

    // set the blend mode so we don't blend into the previous pixels, instead we copy over them.
    CGContextSetBlendMode(cgctx, kCGBlendModeCopy);

    // Draw the image to the bitmap context. Once we draw, the memory 
    // allocated for the context for rendering will then contain the 
    // raw image data in the specified color space.
    CGContextDrawImage(cgctx, rect, theImage.CGImage); 

    // Now we can get a pointer to the image data associated with the bitmap
    // context.
    m_pPixelData = reinterpret_cast<char*>(CGBitmapContextGetData (cgctx));

    m_bytesPerPixel = 4;

    // any client using the photo processing package is required to implement SELECT_PHOTO to its CLIENT_STATE
    m_pClient->SetState( GEClient::LOADING_PHOTO );

    m_pClient->PassPixelDataFromCamera( m_pPixelData, m_photoWidth, m_photoHeight, m_bytesPerPixel );

    // When finished, release the context
    CGContextRelease(cgctx); 
}


CGContextRef CreateARGBBitmapContextPopOver (CGImageRef inImage, UIImagePickerControllerSourceType sourceType, GEClient* pClient )
{
    CGContextRef    context = NULL;
    CGColorSpaceRef colorSpace;
    void *          bitmapData;
    int             bitmapByteCount;
    int             bitmapBytesPerRow;

// Get image width, height. We'll use the entire image.
    size_t pixelsWide = CGImageGetWidth(inImage);
    size_t pixelsHigh = CGImageGetHeight(inImage);
    NSLog(@"Camera resolution:%lu x %lu", pixelsWide, pixelsHigh );


    // Declare the number of bytes per row. Each pixel in the bitmap in this
    // example is represented by 4 bytes; 8 bits each of red, green, blue, and
    // alpha.
    bitmapBytesPerRow   = (pixelsWide * 4);
    bitmapByteCount     = (bitmapBytesPerRow * pixelsHigh);

    // Use the generic RGB color space.
    colorSpace = CGColorSpaceCreateDeviceRGB();
    if (colorSpace == NULL)
    {
        fprintf(stderr, "Error allocating color space\n");
        return NULL;
    }

    // Allocate memory for image data. This is the destination in memory
    // where any drawing to the bitmap context will be rendered.
    bitmapData = malloc( bitmapByteCount );
    if (bitmapData == NULL) 
    {
        fprintf (stderr, "Memory not allocated!");
        CGColorSpaceRelease( colorSpace );
        return NULL;
    }

    // Create the bitmap context. We want pre-multiplied ARGB, 8-bits 
    // per component. Regardless of what the source image format is 
    // (CMYK, Grayscale, and so on) it will be converted over to the format
    // specified here by CGBitmapContextCreate.
    context = CGBitmapContextCreate (bitmapData,
                                 pixelsWide,
                                 pixelsHigh,
                                 8,      // bits per component
                                 bitmapBytesPerRow,
                                 colorSpace,
                                 kCGImageAlphaPremultipliedFirst);
    if (context == NULL)
    {
        free (bitmapData);
        fprintf (stderr, "Context not created!");
    }

 // Make sure and release colorspace before returning
    CGColorSpaceRelease( colorSpace );

    return context;
}

【问题讨论】:

    标签: objective-c ipad ios4 photo


    【解决方案1】:

    我在这里大发雷霆。你的代码看起来不错,我很累,可能会遗漏一些东西,但只是为了测试,删除行“[pImagePicker release];”看看会发生什么。这不是最终建议,但值得一试。

    我在使用图像选择器时遇到了类似的问题,并更改了发布位置并解决了问题。

    【讨论】:

    • 虽然这确实有效,但它会产生两个内存泄漏,一个 32K 泄漏和一个 128k 泄漏。我将它们添加到主要问题中。
    【解决方案2】:

    当您关闭弹出框时,将弹出框控制器的委托设置为 nil。我认为框架代码中有一些东西在你的 viewController 发布后试图访问它的委托方法。从弹出窗口中删除代表为我解决了这个问题,并且我能够正确释放弹出窗口。不知道为什么它只发生在 4,但你去了。希望这能解决您的问题,即使是一个月后。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多