Я получаю EXC_BAD_ACCESS, когда MaxConcurrentOperationCount> 1 - PullRequest
0 голосов
/ 30 марта 2010

Здравствуйте, я использую NSOperationQueue для загрузки изображений в фоновом режиме. Я создал пользовательскую NSOperation для загрузки изображений. Я положил изображения в ячейках таблицы. Проблема заключается в том, что если я делаю [operationQueue setMaxConcurrentOperationCount: 10] и прокручиваю несколько ячеек, программа вылетает с EXC_BAD_ACCESS. Каждый раз, когда он падает в одном и том же месте таблицы. Есть 3 ячейки одна за другой, предназначенные для одной и той же компании и имеющие один и тот же логотип, поэтому в основном следует загружать изображения 3 раза. Каждый второй раз все работает нормально.


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

    NSURL *url = [[NSURL alloc] initWithString:self.imageURL];
    debugLog(@"downloading image: %@", self.imageURL);
    //NSError *error = nil;
    NSData *data = [[NSData alloc] initWithContentsOfURL:url];
    [url release];

    UIImage *image = [[UIImage alloc] initWithData:data];
    [data release];
    if (image)
    {
        if (image.size.width != ICONWIDTH && image.size.height != ICONHEIGHT)
        {
            UIImage *resizedImage;
            CGSize itemSize = CGSizeMake(ICONWIDTH, ICONHEIGHT);

                        //!!! UIGraphicsBeginImageContext NOT THREAD SAFE
            UIGraphicsBeginImageContext(itemSize);
            CGRect imageRect = CGRectMake(0.0, 0.0, itemSize.width, itemSize.height);
            [image drawInRect:imageRect];
            resizedImage = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();

            self.theImage = resizedImage;
        }
        else
        {
            self.theImage = image;
        }
        [image release];
    }
    [delegate didFinishDownloadingImage: self];
    [pool release];
}

Вот так я справляюсь с загрузкой изображений. Если я закомментирую [delegate didFinishDownloadingImage: self]; в приведенной выше функции он не падает, но, конечно, он бесполезен.


-(void) didFinishDownloadingImage:(ImageDownloadOperation *) imageDownloader
{
    [self performSelectorOnMainThread: @selector(handleDidFinishDownloadingImage:) withObject: imageDownloader waitUntilDone: FALSE];
}

-(void) handleDidFinishDownloadingImage:(ImageDownloadOperation *)imageDownloadOperation
{
    NSArray *visiblePaths = [self.myTableView indexPathsForVisibleRows];
    CompanyImgDownloaderState *stateObject = (CompanyImgDownloaderState *)[imageDownloadOperation stateObject];

    if ([visiblePaths containsObject: stateObject.indexPath])
    {
        //debugLog(@"didFinishDownloadingImage %@ %@", imageDownloader.theImage);

        UITableViewCell *cell = [self.myTableView cellForRowAtIndexPath: stateObject.indexPath];
        UIImageView *imageView = (UIImageView *)[cell viewWithTag: 1];
        if (imageDownloadOperation.theImage)
        {
            imageView.image = imageDownloadOperation.theImage;
            stateObject.company.icon = imageDownloadOperation.theImage;
        }
        else
        {
            imageView.image = [(TestWebServiceAppDelegate *)[[UIApplication sharedApplication] delegate] getCylexIcon];
            stateObject.company.icon = [(TestWebServiceAppDelegate *)[[UIApplication sharedApplication] delegate] getCylexIcon];
        }

    }
}


Ответы [ 3 ]

4 голосов
/ 30 марта 2010

Согласно этому сообщению в списке рассылки, UIGraphicsBeginImageContext не является поточно-ориентированным. В сообщении указывается, что CGBitmapContextCreate и связанные с ним функции являются безопасным способом сделать это.

http://osdir.com/ml/cocoa-dev/2009-10/msg00035.html

1 голос
/ 30 марта 2010

Я думаю, что у вас сбой, потому что вы пытаетесь получить доступ к ячейкам в табличном представлении, которых там нет.

Независимо от того, какова длина логической таблицы, визуальное представление таблицы содержит только достаточное количество ячеек для отображения раздела логической таблицы, отображаемой в данный момент на экране. При размере строки по умолчанию таблица показывает и, следовательно, содержит только от 9 до 10 объектов ячеек. Например, если у вас есть логическая таблица длиной 100 строк, и в вашем представлении отображаются строки 11-20, в таблице есть только 9 ячеек. Если вы показываете строки 89-98, он имеет только точно такие же 9 объектов ячеек. Независимо от того, какие строки вы отображаете, вы снова и снова видите одни и те же 9 ячеек. Единственное, что меняется, это данные, которые они отображают.

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

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

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

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

Хорошо, похоже, это решено благодаря codewarrior . Это та часть, которая изменилась.

@synchronized(delegate)
{
    UIImage *resizedImage;
    UIGraphicsBeginImageContext(itemSize);
    CGRect imageRect = CGRectMake(0.0, 0.0, itemSize.width, itemSize.height);
    [image drawInRect:imageRect];
    resizedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    self.theImage = resizedImage; 
}
...