NSURLSessionDataTask不会更新我的表

时间:2014-08-23 15:39:11

标签: objective-c multithreading uitableview

我有一个get xml类并解析它。

+ (instancetype)sharedRssNewsLoader
{
    static BGMRssNewsLoader *sharedRssNewsLoader = nil;    

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{
        sharedRssNewsLoader = [[self alloc] initPrivate];
    });

    return sharedRssNewsLoader;
}


- (instancetype)initPrivate
{
    self = [super init];

    //did the superclass's designated initializer succeed?
    if (self)
    {
        self.rssNewsItems = [[NSMutableArray alloc] init];     

        NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];

        _session = [NSURLSession sessionWithConfiguration:config
                                                 delegate:nil
                                            delegateQueue:nil];

        //RSS call
        [self fetchFeed];        
    }
    return self;
}

- (void)fetchFeed
{
    NSString *requestString = @"http://xxx...";
    NSURL *url = [NSURL URLWithString:requestString];
    NSURLRequest *req = [NSURLRequest requestWithURL:url];

    NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:req
                                                     completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
                                                         self.rssXmlString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
                                                         DDLogVerbose(@"rssXmlString: %@", self.rssXmlString);
                                                         dispatch_sync(dispatch_get_main_queue(), ^{
                                                             [self parseXML:self.rssXmlString];
                                                         });}
                                      ];

    [dataTask resume];
}

//parse XML
- (void)parseXML:(NSString *)source
{
    NSError *error = nil;

    DDXMLDocument *theDocument = [[DDXMLDocument alloc] initWithXMLString:source
                                                                  options:0
                                                                    error:&error];

    NSArray *xmlItems = [theDocument nodesForXPath:@"//item"
                                             error:&error];

    for(DDXMLElement *itemElement in xmlItems)
    {
        NSString *itemTitleValue = [[[itemElement elementsForName:@"title"] lastObject] stringValue];
        NSString *itemDescriptionValue = [[[itemElement elementsForName:@"title"] lastObject] stringValue];

        BGMRssNewsEntry *rssEntry = [[BGMRssNewsEntry alloc] initRssNewsEntryWithTitle:itemTitleValue
                                                                               rssText:itemDescriptionValue
                                                                                rssUrl:nil
                                                                               rssDate:nil
                                                                             isRssRead:NO];

        [self.rssNewsItems addObject:rssEntry];
    }

}

//if a programmer calls [[BGMItemsStore alloc] init], let him know the error of his ways
- (instancetype) init
{
    @throw [NSException exceptionWithName:@"Singleton"
                                   reason:@"You are wrong! Use +[BBGMRssNewsLoader sharedRssNewsLoader]"
                                 userInfo:nil];

    return nil;
}

我想在表格中显示结果。所以在我做的其他课程中:

- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];

    if (self)
    {
        //custom initialization
        //initialization sharedRssNewsLoader with rssItems array and RSS call
        [BGMRssNewsLoader sharedRssNewsLoader];
    }

    return self;
}

但我的表格绝对清楚。我知道当我使用NSURLSessionDataTask时我需要切换到main_queue,我做到了。我认为我搞乱线程:(

1 个答案:

答案 0 :(得分:0)

BGMRssNewsLoader异步加载数据,parseXML正在向self.rssNewsItems对象添加条目。但是,当这个异步请求完成时,我不知道它告诉表重新加载自己的位置。如果表视图的数据源尝试立即检索rssNewsItems数组,则在异步请求完成之前它可能为空。

通常,当您完成加载数据时,您需要调用[self.tableView reloadData](通过在BGMRssNewsLoader实例化期间提供tableview作为参数,或者通过它会发布表视图控制器正在观察的通知,或者提供一个完成块参数,您可以在其中执行reloadData)。

我还建议临时添加一个断点或日志语句,将对象添加到rssNewsItems数组,以确保成功检索和解析XML提要。


例如,我可能会退出fetch并定义一个类似的方法:

- (void)fetchFeedWithCompletionBlock:(void (^)(NSArray *items, NSError *error))completion
{
    NSString *requestString = @"http://xxx...";
    NSURL *url = [NSURL URLWithString:requestString];
    NSURLRequest *req = [NSURLRequest requestWithURL:url];

    NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:req completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
        self.rssXmlString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        DDLogVerbose(@"rssXmlString: %@", self.rssXmlString);
        dispatch_sync(dispatch_get_main_queue(), ^{
            [self parseXML:self.rssXmlString];

            if (completion) {
                completion(self.rssNewsItems, nil);
            }
        });
    }];

    [dataTask resume];
}
顺便说一下,我不会initPrivate方法启动fetch。您希望调用者能够提供它想要的任何完成块。

然后让tableview的控制器做类似的事情:

[[BGMRssNewsLoader sharedRssNewsLoader] fetchFeedWithCompletionBlock:^(NSArray *items, NSError *error) {
    // use the items array and/or error
    [self.tableView reloadData];
}];

显然,fetchFeedWithCompletionBlock实际上应该检测到错误并使用适当的NSError调用完成块(因此表视图可以决定它是如何呈现错误的...单例不应该&#39}对于UI本身做任何事情),但希望这说明了一个网络对象的想法,它提供了一个完成块,允许调用者在异步请求完成时指定它想要做什么。