Разработка iPhone: освобожденный указатель не выделен - PullRequest
19 голосов
/ 15 сентября 2009

я получил это сообщение от отладчика:

Pixture(1257,0xa0610500) malloc: *** error for object 0x21a8000: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug

так что я немного отследил и получил:

(gdb) shell malloc_history 1257 0x21a8000

ALLOC 0x2196a00-0x21a89ff [size=73728]: thread_a0610500 |start | main | UIApplicationMain | GSEventRun | GSEventRunModal | CFRunLoopRunInMode | CFRunLoopRunSpecific | __CFRunLoopDoObservers | CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) | CA::Transaction::commit() | CA::Context::commit_transaction(CA::Transaction*) | CALayerDisplayIfNeeded | -[CALayer _display] | CABackingStoreUpdate | backing_callback(CGContext*, void*) | -[CALayer drawInContext:] | -[UIView(CALayerDelegate) drawLayer:inContext:] | -[AvatarView drawRect:] | -[AvatarView overlayPNG:] | +[UIImageUtility createMaskOf:] | UIGraphicsGetImageFromCurrentImageContext | CGBitmapContextCreateImage | create_bitmap_data_provider | malloc | malloc_zone_malloc

и я действительно не могу понять, что я делаю неправильно. вот код функции [UIImageUtility createMaskOf:]:

+ (UIImage *)createMaskOf:(UIImage *)source {
    CGRect rect = CGRectMake(0, 0, source.size.width, source.size.height);
    UIGraphicsBeginImageContext(CGSizeMake(source.size.width, source.size.height));
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextTranslateCTM(context, 0, source.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);

    UIImage *original = [self createGrayCopy:source];

    CGContextRef context2 = CGBitmapContextCreate(NULL, source.size.width, source.size.height, 8, 4 * source.size.width, 
                                                   CGColorSpaceCreateDeviceRGB(), kCGImageAlphaNoneSkipLast);
    CGContextDrawImage(context2, CGRectMake(0, 0, source.size.width, source.size.height), original.CGImage);
    CGImageRef unmasked = CGBitmapContextCreateImage(context2);

    const float myMaskingColorsFrameColor[6] = { 1,256,1,256,1,256 };
    CGImageRef mask = CGImageCreateWithMaskingColors(unmasked, myMaskingColorsFrameColor);

    CGContextSetRGBFillColor (context, 256,256,256, 1);
    CGContextFillRect(context, rect);
    CGContextDrawImage(context, rect, mask);

    UIImage *whiteMasked = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return whiteMasked;
}

другая пользовательская функция, вызываемая до этого, следующая:

- (UIImage *)overlayPNG:(SinglePart *)sp {
    NSLog([sp description]);
    // Rect and context setup
    CGRect rect = CGRectMake(0, 0, sp.image.size.width, sp.image.size.height);
    NSLog(@"%f x %f", sp.image.size.width, sp.image.size.height);

    // Create an image of a color filled rectangle
    UIImage *baseColor = nil;
    if (sp.hasOwnColor) {
            baseColor = [UIImageUtility imageWithRect:rect ofColor:sp.color];
    } else {
        SinglePart *facePart = [editingAvatar.face.partList objectAtIndex:0];
        baseColor = [UIImageUtility imageWithRect:rect ofColor:facePart.color];
    }

    // Crete the mask of the layer
    UIImage *mask = [UIImageUtility createMaskOf:sp.image];
    mask = [UIImageUtility createGrayCopy:mask];

    // Create a new context for merging the overlay and a mask of the layer
    UIGraphicsBeginImageContext(CGSizeMake(sp.image.size.width, sp.image.size.height));
    CGContextRef context2 = UIGraphicsGetCurrentContext(); 

    // Adjust the coordinate system so that the origin 
    // is in the lower left corner of the view and the 
    // y axis points up 
    CGContextTranslateCTM(context2, 0, sp.image.size.height); 
    CGContextScaleCTM(context2, 1.0, -1.0); 

    // Create masked overlay color layer
    CGImageRef MaskedImage = CGImageCreateWithMask (baseColor.CGImage, mask.CGImage);

    // Draw the base color layer
    CGContextDrawImage(context2, rect, MaskedImage);

    // Get the result of the masking
    UIImage* overlayMasked = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    UIGraphicsBeginImageContext(CGSizeMake(sp.image.size.width, sp.image.size.height));
    CGContextRef context = UIGraphicsGetCurrentContext();

    // Adjust the coordinate system so that the origin 
    // is in the lower left corner of the view and the 
    // y axis points up 
    CGContextTranslateCTM(context, 0, sp.image.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);

    // Get the result of the blending of the masked overlay and the base image
    CGContextDrawImage(context, rect, overlayMasked.CGImage);

    // Set the blend mode for the next drawn image
    CGContextSetBlendMode(context, kCGBlendModeOverlay);

    // Component image drawn
    CGContextDrawImage(context, rect, sp.image.CGImage);

    UIImage* blendedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    CGImageRelease(MaskedImage);

    return blendedImage;
}

Ответы [ 7 ]

27 голосов
/ 28 сентября 2009

У меня была такая же проблема, с множеством одинаковых симптомов:

  • освобождаемый указатель не был выделен ошибка
  • обновлен до Xcode 3.2
  • ошибка в коде для изображений

Если я изменю цель сборки на 3.1, ошибки в симуляторе исчезнут. Если я запускаю код на устройстве, ошибки не появляются. Возможно ошибка в 3.0

Мой совет - протестируйте с 3.1 в качестве цели, и если хотите, вы можете собрать 3.0 для релиза и не беспокоиться об ошибках, поскольку они не происходят на устройстве.

22 голосов
/ 15 сентября 2009

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

Существует два основных типа ошибок памяти:

  1. Выделение больше, чем вы бесплатно.
  2. Освобождает больше, чем вы выделяете.

В этом случае похоже, что вы освобождаете слишком много, что легче увидеть (т.к. он может потерпеть крах раньше), но сложнее отследить.

Вот стратегия, которую вы можете использовать, чтобы помочь найти дополнительные освобождения:

  • Отключите некоторые из ваших освобождений.
  • Проверьте, происходит ли сбой.

В идеале вы можете найти одну единственную команду освобождения, которая, если ее удалить, позволит вашему коду работать правильно. Может быть полезно попробовать подход бинарного поиска, если это целесообразно для вашей кодовой базы. Если это большая кодовая база, мы надеемся, что вы используете контроль версий и можете попытаться сосредоточиться на последних разностях.

Имейте в виду, что освобождение может быть вызвано несколькими различными способами здесь. Скорее всего, вы звоните release и autorelease на объекты. Также возможно, что вы явно вызываете dealloc (что обычно является ошибкой). И, конечно, вы можете даже напрямую звонить free.

После того, как вы избавились от лишних выделений, неплохо бы также проверить утечки памяти (или дополнительные выделения). Вы можете сделать это с помощью инструментов и других инструментов. Хорошее место для начала - прочитать Поиск утечек памяти в руководстве по разработке для iPhone.

Добавлено: Также неплохо установить указатель на nil сразу после того, как вы его отпустили и покончили с ним. Таким образом, если вы позвоните [objectPtr release]; позже, он ничего не сделает.

(PS Кстати, мой самый забавный тип ошибки # 1 - повреждение памяти в многопоточном коде. Одна из них была однажды в многомиллионной базе кода).

3 голосов
/ 15 сентября 2009

Хотя это, вероятно, не является причиной вашего сбоя, вы теряете память, не освобождая объекты context2, unmasked и mask Core Foundation, используя CFRelease(), CFImageRelease() или тому подобное.

0 голосов
/ 01 июня 2010

Это может быть только я, но вы не можете сделать следующее?

UIImage *whiteMasked = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

return whiteMasked;

whiteMasked размещается в стеке функции и после ее возврата указатель больше не действителен? Или я что-то упустил? Если вы используете возвращенный UIImage *, он еще не гарантирован. (это будет хит и мисс). Разве вам не нужно выделять UIImage *, а затем автоматически освобождать его перед возвратом?

0 голосов
/ 06 апреля 2010

Я боролся с той же ошибкой в ​​моем коде. Что меня озадачило, так это то, что мое приложение работало в OS 3.0 без каких-либо проблем, пока я не внес небольшую модификацию в код, не имеющий ничего общего с CGImage *. Но как только он начал терпеть неудачу, он никогда не работал без сбоев. Когда я перешел на 3.1, все снова заработало. Я сузил ошибку до вызова CGImageRelease (). Удаление этой строки или добавление сохранения в результирующий UIImage решило проблему - хотя это не является решением, так как приложение будет терять память.

Я пытался использовать NSZombie с инструментами. Это не помогло - приложение упало без обнаружения зомби.

Кроме того, собственные примеры приложений Apple (например, TheElements) НЕ дают сбой, а используют тот же EXACT-код, что и мое приложение. Итак, я изо всех сил пытаюсь принять проблему, лежащую в основе. Сейчас я переключаюсь на 3.1 и продолжаю.

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

Я просто хотел еще раз подтвердить, что получаю:

освобожденный указатель не выделен

ошибка, и она исчезла, если я изменил свою целевую ОС на 3.1 вместо 3.0

0 голосов
/ 24 сентября 2009

У меня была та же проблема со следующим кодом.

-(void)adjustImageToImageView:(UIImage*)img{

float numPixels = 100;
float radius = 5;
UIGraphicsBeginImageContext(CGSizeMake(numPixels, numPixels));
CGContextRef c = UIGraphicsGetCurrentContext();

CGContextBeginPath(c);
CGContextMoveToPoint  (c, numPixels, numPixels/2);
CGContextAddArcToPoint(c, numPixels, numPixels, numPixels/2, numPixels,   radius);
CGContextAddArcToPoint(c, 0,         numPixels, 0,           numPixels/2, radius);
CGContextAddArcToPoint(c, 0,         0,         numPixels/2, 0,           radius);
CGContextAddArcToPoint(c, numPixels, 0,         numPixels,   numPixels/2, radius);
CGContextClosePath(c);

CGContextClip(c);

[img drawAtPoint:CGPointZero];
UIImage *converted = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.imageView.image = converted;   }

Я взял эту функцию из приложения Twitterfon с открытым исходным кодом.

Когда я пытался решить проблему, я пытался изменить последнюю строку на

self.imageView.image = [converted retain]

И это остановило сообщения об ошибках в консоли. Я скоро проверю это в «Утечках», чтобы увидеть, что происходит.

...