1

我正在使用 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];
    }

}
}

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,或者我应该使用其他方法吗?

4

1 回答 1

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;

当你看到多行像这样做同样的事情时,这表明你可以把它拉出来做不同的事情。例如,您始终可以将 the 设置album.albumOrderTitlealbumTitle,然后仅在专辑标题长度大于 4 且前缀为 @"The" 时才执行不同的操作。

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

cellForRowAtIndexPath:同样令人费解:

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

您应该使用UITableView's cell-reuse 机制。

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,这意味着你总是会定义相同的谓词,这意味着你总是会得到相同的predict数组。

Album *album1 = [predict objectAtIndex:indexPath.row];
albumCell.titleLabel.text = album1.albumTitle;
albumCell.textLabel.text = album1.albumArtist;
if (album1.albumArtwork) {
    albumCell.avatarImageView.image = album1.albumArtwork;
} else {
    albumCell.avatarImageView.image = [UIImage imageNamed:@"albumArtInvertedLight1.png"];
}

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

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

于 2013-05-08T22:40:43.760 回答