NSAutoreleasePool выдает на iPhone - PullRequest
0 голосов
/ 29 июля 2010

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

- (void) loadImageTest
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    NSURL *url = [[NSURL alloc] init];
    url = [NSURL URLWithString:logoPath];

    NSData *data = [[NSData alloc] init];
    data = [NSData dataWithContentsOfURL:url];

    loadingImage = [UIImage imageWithData:data];
    titleLogoImage.image = loadingImage;

    //[pool drain];
    [pool release];
}

Вызывается из этой строки кода в инициализации нового представления:

[NSThread detachNewThreadSelector:@selector(loadImageTest) toTarget:self withObject:nil];

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

Спасибо

Ответы [ 3 ]

2 голосов
/ 29 июля 2010

Ваш:

// Это нормально, вы можете попробовать использовать асинхронные методы NSURLConnections вместо // ваш собственный поток [NSThread detachNewThreadSelector: @selector (loadImageTest) toTarget: self withObject: nil];

- (void)loadImageTest
{
    // This is fine
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    // you're making and then abandoning this url object so it will leak
    NSURL *url = [[NSURL alloc] init];
    // this is fine
    url = [NSURL URLWithString:logoPath];
    // again making and abandoning an object
    NSData *data = [[NSData alloc] init];
    data = [NSData dataWithContentsOfURL:url];
    // this works, but is not thread safe,
    // can't operate on UIKit objects off the
    // main thread
    loadingImage = [UIImage imageWithData:data];
    titleLogoImage.image = loadingImage;
    // this is fine
    //[pool drain];
    [pool release];
}

Очистить, чтобы обеспечить безопасность потоков и т. Д. Должно помочь:

// I'm assuming you have a iVar for logoPath but we don't want to
// make threaded calls to an iVar (it's not mutable, so you could do it, but it's just bad form)
// If i'm wrong about logoPath being an iVar don't sweat copying it.
- (void)whateverMethodYouWant
{
    NSString *aLogoPath = [[logoPath copy] autorelease];
    [NSThread detachNewThreadSelector:@selector(loadImageForPath:) toTarget:self withObject:aLogoPath];
}
- (void)loadImageForPath:(NSString *)aLogoPath
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:aLogoPath]];
    // the performSelector will retain the data until the method can be performed
    [self performSelectorOnMainThread:@selector(setImageForTitleLogo:) withObject:imgData waitUntilDone:NO];

    [pool release];
}
- (void)setImageForTitleLogo:(NSData *)imgData
{
    // This part is not strictly necessary
    // but it's a nice way to wrap a method that needs to happen on the main thread.
    if ([NSThread isMainThread])
    {
        // make the image (i'm assuming you meant loadingImage to be a local scope variable)
        UIImage *loadingImage = [UIImage imageWithData:imgData];
        // make sure the button still exists 
        // also make sure you're setting any references to this button to nil when you're releasing and making new views
        // so that you're not addressing a button that's been released
        // (assigning the image to the button will cause the button to retain it for you)
        if (titleLogoImage != nil)
            titleLogoImage.image = loadingImage;
    }
    else
    {
        [self performSelectorOnMainThread:@selector(setImageForTitleLogo:) withObject:imgData waitUntilDone:NO];
    }
}
0 голосов
/ 29 июля 2010

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

Однако, помимо проблем, описанных mvds , у вас нет острой необходимости в локализованном пуле авто-релизов.Это ничего не даст здесь, но доставит вам неприятности.

Авто-релизные пулы опасны и не для начинающих.Они будут убивать любые автоматически выпущенные объекты в них.Как правило, вы создаете свой собственный пул только тогда, когда быстро создаете большое количество объектов, интенсивно использующих память.Это не похоже на случай здесь.

Несмотря на уделенное им внимание, пользовательские пулы используются редко.После более чем десятилетней работы Apple API я, вероятно, могу подсчитать, сколько раз я использовал свой собственный пул.

0 голосов
/ 29 июля 2010

Вы делаете странные вещи.

NSURL *url = [[NSURL alloc] init];

означает, что вы создаете NSURL объект, которым вы владеете.

url = [NSURL URLWithString:logoPath];

Это означает, что вы создаете другой NSURL объект, который выдается автоматически.NSURL, который вы только что создали, просто протекает.То же самое относится и к NSData.

loadingImage должен быть сохранен titleLogoImage, иначе он будет освобожден на канале вашего NSAutoReleasePool.Что такое titleLogoImage и сохраняет ли оно image?

edit ps: меня также беспокоит то, что loadingImage не ограничивается областью действия функции.Короче говоря:

NSURL *url = [NSURL URLWithString:logoPath];
NSData *data = [NSData dataWithContentsOfURL:url];
titleLogoImage.image = [UIImage imageWithData:data];

может сэкономить некоторые головные боли, по крайней мере.

...