使用GCD时,UITableViewCell的数据正在混淆

时间:2013-05-07 21:30:02

标签: ios objective-c uitableview cocoa-touch uikit

我正在使用GCD在后台线程上加载我的UITableView数据,但这样做会混淆我的自定义UITableViewCell中的数据。单元格上的titleLabel和imageView很好,但textLabel(副标题)在每个单元格上都是错误的。当数据加载到主线程上时,这不会发生,并且数据不是来自多个数组,因此我只能猜测它是因为我使用了GCD,我是新手。

首先,我设置了NSOperationQueue ......

- (void)setUpTableForAlbums
{
   dispatch_async(dispatch_get_global_queue(0, 0), ^
               {
                   [self setUpTableForAlbumsFD];
                   dispatch_async(dispatch_get_main_queue(), ^
                                  {
                                      [albumTable reloadData];
                                  });
               });
}

setUpTableForAlbumsFD选择器如此......

- (void)setUpTableForAlbumsFD
{
//    __block CLProgressIndeterminateView *clP = [[CLProgressIndeterminateView alloc] initWithFrame:CGRectMake(325, tableScrollView.frame.size.height/2, 310, 20)];
//    [tableScrollView addSubview:clP];
//    [clP startAnimating];
type = @"Albums";
queryAlbums = [MPMediaQuery albumsQuery];
[queryAlbums setGroupingType:MPMediaGroupingAlbum];

mainArrayAlbum = [[NSMutableArray alloc] init];
otherArrayAlbum = [[NSMutableArray alloc] init];
theOtherArrayAlbum = [[NSMutableArray alloc] init];

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];

NSArray *fullArray = [queryAlbums collections];
for (MPMediaItemCollection *collection in fullArray)
{
    item = [collection representativeItem];
    NSString *albumName = [item valueForProperty:MPMediaItemPropertyAlbumTitle];
    NSString *albumArtist = [item valueForProperty:MPMediaItemPropertyArtist];

    NSString *filePath = [documentsPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.png", albumName]];
    Album *album = [[Album alloc] init];
    album.albumTitle = albumName;
    album.albumArtwork = [UIImage imageImmediateLoadWithContentsOfFile:filePath];
    if (album.albumTitle.length > 4)
    {
        if ([album.albumTitle hasPrefix:@"The "])
        {
            album.albumOrderTitle = [album.albumTitle substringFromIndex:4];
        }
        else
        {
            album.albumOrderTitle = album.albumTitle;
        }
    }
    else
    {
        album.albumOrderTitle = album.albumTitle;
    }
    album.albumArtist = albumArtist;
    if (![mainArrayAlbum containsObject:album])
    {
        [mainArrayAlbum addObject:album];
    }

}
}

相册自定义类只是数据的容器。

cellForRowAtIndex路径方法同样如此......

MasterCellAlbum *albumCell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    if (!albumCell)
    {
        albumCell = [[MasterCellAlbum alloc] initWithStyle:nil reuseIdentifier:@"Cell"];
    }
    alphabet = [self alphabet:@"album" withIndex:YES];
    [albumCell setSelectionStyle:UITableViewCellEditingStyleNone];
    NSString *alpha = [alphabet objectAtIndex:indexPath.section];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.albumOrderTitle beginswith[c] %@", alpha];
    NSArray *predict = [mainArrayAlbum filteredArrayUsingPredicate:predicate];
    [cell setSelectionStyle:UITableViewCellSelectionStyleNone];


    Album *album1 = [predict objectAtIndex:indexPath.row];
    albumCell.titleLabel.text = album1.albumTitle;
    albumCell.textLabel.text = album1.albumArtist;
    albumCell.avatarImageView.image = album1.albumArtwork;

    longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(albumLittleMenu:)];
    [albumCell addGestureRecognizer:longPress];
    return albumCell;

我是否正确使用GCD,还是我应该采用其他方式?

1 个答案:

答案 0 :(得分:1)

让人惊讶。关于这段代码,我们有很多事情可以说是有趣的。让我们从第一种方法开始:

NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
NSInvocationOperation *operation = [NSInvocationOperation alloc];

operation = [operation initWithTarget:self selector:@selector(setUpTableForAlbumsFD) object:nil];
[operation setCompletionBlock:^
{
  [albumTable reloadData];
}];
[operationQueue addOperation:operation];
operation = nil;

我认为你要做的是在后台执行-setUpTableForAlbumsFD方法,然后在完成后重新加载tableView。

首先,completionBlock不在主线程上执行(你必须从中调用-reloadData)。文档说:

  

无法保证完成块的确切执行上下文,但通常是辅助线程。因此,您不应该使用此块来执行任何需要非常特定的执行上下文的工作。

执行此方法的更简单方法是:

dispatch_async(dispatch_get_global_queue(0,0), ^{
  [self setUpTableForAlbumsFD];
  dispatch_async(dispatch_get_main_queue(), ^{
    [albumTable reloadData];
  }
});

现在适用于setUpTableForAlbumsFD方法......

- (void)setUpTableForAlbumsFD {
    type = @"Albums";
    queryAlbums = [MPMediaQuery albumsQuery];
    [queryAlbums setGroupingType:MPMediaGroupingAlbum];

    mainArrayAlbum = [[NSMutableArray alloc] init];

    NSArray *fullArray = [queryAlbums collections];
    for (MPMediaItemCollection *collection in fullArray) {
        item = [collection representativeItem];
        NSString *albumName = [item valueForProperty:MPMediaItemPropertyAlbumTitle];
        NSString *albumArtist = [item valueForProperty:MPMediaItemPropertyArtist];

        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsPath = [paths objectAtIndex:0];

为了提高效率,您应该在NSDocumentDirectory循环之外找到这两行for

        NSString *filePath = [documentsPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.png", albumName]];

        UIImage *artwork = [UIImage imageImmediateLoadWithContentsOfFile:filePath];

我假设这是一个UIImage类别方法?

        Album *album = [[Album alloc] init];
        album.albumTitle = albumName;
        if (album.albumTitle.length > 4) {
            if ([[NSString stringWithFormat:@"%c%c%c%c", [album.albumTitle characterAtIndex:0], [album.albumTitle characterAtIndex:1], [album.albumTitle characterAtIndex:2], [album.albumTitle characterAtIndex:3]] isEqual: @"The "]) {

糟糕!只需:if ([album.albumTitle hasPrefix:@"The "]) {

                album.albumOrderTitle = [album.albumTitle substringWithRange:NSMakeRange(4, album.albumTitle.length-4)];

这里有:album.albumOrderTitle = [album.albumTitle substringFromIndex:4];

            } else {
                album.albumOrderTitle = album.albumTitle;
            }
        } else {
            album.albumOrderTitle = album.albumTitle;

当你看到多条线路正在做同样的事情时,这是一个标志,你可以把它拉出来并以不同的方式做。例如,您可以始终album.albumOrderTitle设置为albumTitle,然后如果albumTitle长度超过4并且前缀为@“,则只执行不同的操作“。

        }
        album.albumArtist = albumArtist;
        album.albumArtwork = artwork;
        if (![mainArrayAlbum containsObject:album]) {
            [mainArrayAlbum addObject:album];
        }
    }
}

您的cellForRowAtIndexPath:同样错综复杂:

MasterCellAlbum *albumCell = [[MasterCellAlbum alloc] init];

您应该使用UITableView的细胞重用机制。

alphabet = [self alphabet:@"album" withIndex:YES];
[albumCell setSelectionStyle:UITableViewCellEditingStyleNone];
NSString *alpha = [alphabet objectAtIndex:indexPath.section];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.albumOrderTitle beginswith[c] %@", alpha];
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];

NSArray *predict = [mainArrayAlbum filteredArrayUsingPredicate:predicate];

为什么要在每个时间内重新过滤mainArrayAlbum 所需的单元格?看起来你总是会抓住相同的alphabet,这意味着你总是会定义相同的谓词,这意味着你总是会以相同的{{1}结束数组。

predict

因此,有一些显而易见的地方,您的代码可以使用一些改进。老实说,我认为你遇到的问题的答案是因为你试图在后台线程上重新加载tableview,这是一个坏主意™。