ios - 多个 NSURLSession 导致 UITableView 问题

标签 ios objective-c uitableview nsurlsession

我在这里遇到了一个奇怪的问题。我的一个 NSURLSessions 负责获取我存储的餐厅信息(餐厅名称、餐厅的 logo URL 等),然后第二个 NSURLSession 负责使用餐厅的 logo URL 来检索特定图像并设置它对于每个 UITableView 的单元格。

然而,问题是我的 UITableView 有时根本不加载任何东西,所以单元格是空的,但有时当我添加额外的 [_tableView reload] 时在 fetchPosts 方法的 NSURLSessions 完成 block 中,它会起作用,但是如果我重新运行它,单元格将再次停止显示任何内容。肯定有问题。看看我下面的代码:

#import "MainViewController.h"
#import "SWRevealViewController.h"
#import "RestaurantNameViewCell.h"
#import "RestaurantList.h"

@interface MainViewController ()

@end

@implementation MainViewController

- (void)viewDidLoad
{
    [super viewDidLoad];


    //List of restaurants needed to load home page
    _restaurantInformationArray = [[NSMutableArray alloc] init];


    self.tableView.dataSource = self;
    self.tableView.delegate = self;


    //setup for sidebar
    SWRevealViewController *revealViewController = self.revealViewController;
    if ( revealViewController )
    {
        [self.sidebarButton setTarget: self.revealViewController];
        [self.sidebarButton setAction: @selector( revealToggle: )];
        [self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];
    }



    //Get list of restaurants and their image URLs
    [self fetchPosts];

}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [_restaurantInformationArray count];
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    RestaurantNameViewCell *cell = (RestaurantNameViewCell *)[_tableView dequeueReusableCellWithIdentifier:@"restaurantName" forIndexPath:indexPath];

    RestaurantList *currentRestaurant = [_restaurantInformationArray objectAtIndex:indexPath.row];


    cell.restaurantName.text = currentRestaurant.name;
    cell.imageAddress = currentRestaurant.imageURL;

    cell.restaurantClicked = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapDetected:)];
    cell.restaurantClicked.numberOfTapsRequired = 1;
    cell.restaurantLogo.userInteractionEnabled = YES;
    [cell.restaurantLogo addGestureRecognizer:cell.restaurantClicked];
    cell.restaurantLogo.tag = indexPath.row;

    //Add restaurant logo image:
    NSString *URL = [NSString stringWithFormat:@"http://private.com/images/%@.png",cell.imageAddress];
    NSURL *url = [NSURL URLWithString:URL];

    NSURLSessionDownloadTask *downloadLogo = [[NSURLSession sharedSession]downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
        UIImage *downloadedImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:location]];

        cell.restaurantLogo.image = downloadedImage;
    }];
    [downloadLogo resume];

    return cell;
}



-(void)fetchPosts {
    NSString *address = @"http://localhost/xampp/restaurants.php";
    NSURL *url = [NSURL URLWithString:address];

    NSURLSessionDataTask *downloadRestaurants = [[NSURLSession sharedSession]dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

        NSError *someError;
        NSArray *restaurantInfo = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&someError];


         for(NSDictionary *dict in restaurantInfo) {
             RestaurantList *newRestaurant = [[RestaurantList alloc]init];

             newRestaurant.name = [dict valueForKey:@"name"];
             newRestaurant.imageURL = [dict valueForKey:@"image"];

             [_restaurantInformationArray addObject:newRestaurant];


             //Refresh table view to make sure the cells have info AFTER the above stuff is done
             [_tableView reloadData];
         }
    }];

    [downloadRestaurants resume];
}


@end

这可能是我正在犯的一个非常愚蠢的错误,但我不确定我应该如何纠正这个错误。我是 iOS 开发新手,非常感谢一些指导 :)

最佳答案

除了假设您的网络请求没有出错(如果有网络错误,您至少应该记录),还有线程问题。

您的 NSURLSession回调可能在后台线程上运行。这使得调用 UIKit(又名 - [_tableView reloadData])变得不安全。 UIKit 不是线程安全的。这意味着从另一个线程调用任何 UIKit 的 API 都会产生不确定的行为。您需要在主线程上运行那段代码:

dispatch_async(dispatch_get_main_queue(), ^{
    [_tableView reloadData];
});

同样用于获取图像。由于表格 View 单元重用可能会导致滚动时显示错误的图像,因此它稍微复杂一些。这是因为当用户滚动时,相同的单元格实例用于数组中的多个值。当这些回调中的任何一个触发时,它将替换该单元格中发生的任何图像。重现此问题的一般步骤如下:
  • TableView 请求 5 个单元格
  • MainViewController 请求 5 个图像(每个单元格一个)
  • 用户向下滚动一个单元格
  • 第一个单元被重用为第 6 个单元。
  • MainViewController 为第 6 个单元格请求另一个图像。
  • 检索到第 6 个图像,触发回调,第一个单元格的图像设置为图像 #6。
  • 检索到第一个图像,触发回调,第一个单元格的图像设置为图像#1( 不正确的 )。

  • 在尝试将图像分配给它之前,您需要确保该单元格显示正确的单元格。如果您不想实现在单元格中获取图像的逻辑,您可以使用 SDWebImage反而。使用 SDWebImage 的 [UIImageView sd_setImageWithURL:]是线程安全的(它将 set the image on the main thread )。

    旁注:
  • 一旦您的所有更改都在 _restaurantInformationArray 中,您只需重新加载数据。 ,而不是每次都在 for 循环中。
  • 关于ios - 多个 NSURLSession 导致 UITableView 问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30836094/

    相关文章:

    ios - 我们如何检测并保存 iPhone/iPad 触摸屏上写入的文本?

    ios - 删除/重置核心数据中的所有条目?

    objective-c - 电话:的IOS6.0 UIWebView方案

    objective-c - 如何使用 iOS 5 中的 Twitter 框架获取用户的 Twitter 个人资料信息?

    iphone - 细胞背景及配件

    android - react native - "undefined is not an object react native evaluating this.props.navigation"

    ios - 如何在 tableview 单元格内的 collectionview 单元格的按钮单击上获取索引

    iphone - 无法同时满足约束

    iphone - 在 ViewController 中设置自定义 UITableViewCell 的 UILabel.text

    cocoa-touch - ScrollView 内的 TableView