UIImagePickerControllerDelegate на iPad на iOS4 вылетает (отлично работает в iOS5) - PullRequest
0 голосов
/ 19 февраля 2012

У меня проблема, когда доступ к библиотеке фотографий вызывает сбой ПОСЛЕ того, как я выбрал фотографию.

Выход журнала:

[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 и никогда больше не вызываю его. К сожалению, через несколько кадров появляется ошибка, указанная выше. Только на iPad на iOS4.3. Отлично работает на iPhone (другой объект интерфейса) и на iPad с iOS3.x и iOS5.x

У кого-нибудь есть идеи?

Спасибо!

EDIT - Похоже, что удаление вызова [super dealloc] устраняет проблему. Очевидно, не лучшее решение, но, возможно, поможет выяснить, что происходит? Я также прошел весь мой код, чтобы попытаться выяснить, где он падает, но безрезультатно. Он отклоняет всплывающее окно, проходит полный цикл рендеринга, завершает цикл рендеринга и затем падает в сборке.

РЕДАКТИРОВАТЬ 2 - Следующий код больше не дает сбой на iOS4, но утечка 32 КБ и 128 КБ памяти.

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;
}

Ответы [ 2 ]

0 голосов
/ 19 марта 2012

Установите делегат контроллера поповера на ноль при отклонении поповера. Я думаю, что в коде фреймворка есть что-то, что пытается получить доступ к методу делегата в вашем viewController после его выпуска. Удаление делегата из поповера решило эту проблему для меня, и я смог правильно выпустить поповер. Понятия не имею, почему это происходит только на 4, но вы идете. Надеюсь, это решит вашу проблему, даже если это будет месяц спустя.

0 голосов
/ 20 февраля 2012

Я беру здесь дикий удар. Ваш код выглядит довольно хорошо, я устал и, возможно, чего-то не хватает, но просто для тестирования удалите строку "[pImagePicker release];" и посмотрим, что получится. Это не окончательная рекомендация, но стоит попробовать.

Я столкнулся с чем-то похожим со средством выбора изображений и изменил место, где я делал релиз, и это решило проблему.

...