完成后台提取后,块完成处理程序引用为零

时间:2015-05-23 14:57:59

标签: ios objective-c objective-c-blocks background-process

我正在尝试使用performFetchWithCompletionHandler实现RSS Feed的后台获取,但是当我想调用完成处理程序时,它是零。

我是否错过了保留对self.completionHandler的引用的方法?

我是否正确宣布了self.completionHandler?

app appate中的

        //background fetch new RSS Feeds
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
    UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
    MasterViewController *navigationController = [mainStoryboard instantiateViewControllerWithIdentifier:@"MasterView"];
    MasterViewController *viewController = navigationController;

    [viewController startParsingWithCompletionHandler2: ^ (UIBackgroundFetchResult completionHandler2){
        completionHandler (UIBackgroundFetchResultNewData);
    }];
}
主视图控制器中的

@property (nonatomic, strong) void (^completionHandler)(UIBackgroundFetchResult);


- (void) startParsingWithCompletionHandler2:(void (^)(UIBackgroundFetchResult))completionHandler2
{
    self.completionHandler = completionHandler2;
    if (self.completionHandler) {
        NSLog(@"completionHandler");
    }else{
        NSLog(@"not completionHandler");
    }
    [self performSelector: @selector(stopParsing) withObject: nil afterDelay: PARSER_TIME_LIMIT];
    [self.activityIndicator startAnimating];
    numberOfCompletedStories = 0;
    [self.parserArray removeAllObjects];
                                                        //check for RSS Site data updates
    for (int lCounter = 0; lCounter < self.rssFeedAddresses.count; lCounter ++) {
        RSSParser *parser = [[RSSParser alloc] init];
        [parser setDelegate: self];
        [self.parserArray addObject: parser];
        [parser setSiteTitle: [self.rssFeedNames objectAtIndex: lCounter]];
        [NSThread detachNewThreadSelector: @selector(begin:) toTarget: parser withObject: [self.rssFeedAddresses objectAtIndex: lCounter]];
    }
    if (self.completionHandler) {
        NSLog(@"#2  completionHandler");
    }else{
        NSLog(@"#2  not completionHandler");
    }
}

    - (void) storyIsDone//called when parser completed one rss feed
{
    numberOfCompletedStories ++;
    if (self.completionHandler) {
        NSLog(@"storyIsDone  YES completion handler %i", numberOfCompletedStories);
    }else{
        NSLog(@"storyIsDone  Not completion handler");
    }
    if (numberOfCompletedStories == self.rssFeedAddresses.count)
    {
            //if all the feeds are done cancel time-out timer
        [NSObject cancelPreviousPerformRequestsWithTarget: self selector: @selector(stopParsing) object: nil];
        [self.activityIndicator stopAnimating];
        [self.refreshControl endRefreshing];
        [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(reloadRSSfeeds) name: @"ReloadFeeds" object: nil];
        canRefresh = YES;
        NSLog(@"call back");
        [self performSelectorOnMainThread: @selector(callCompletion) withObject: self waitUntilDone: YES];
    }//else not yet complete
}

- (void) callCompletion
{
    if (self.completionHandler) {
        NSLog(@"callCompletion  YES completion handler");
        self.completionHandler (UIBackgroundFetchResultNewData);

    }else{
        NSLog(@"callCompletion  Not completion handler");
    }
}

输出结果为:

completionHandler
 #2  completionHandler
storyIsDone  Not completion handler
storyIsDone  Not completion handler
storyIsDone  Not completion handler
storyIsDone  Not completion handler
storyIsDone  Not completion handler
storyIsDone  Not completion handler
storyIsDone  Not completion handler
call back
callCompletion  Not completion handler

3 个答案:

答案 0 :(得分:0)

我总是使用(nonatomic, copy)来表示块属性。你可以尝试一下,看看它是否有帮助。

但是,在您的代码中,我认为您可以通过传递给application:performFetchWithCompletionHandler:的完成处理程序。

答案 1 :(得分:0)

在.h文件中定义一个完成处理程序,如下所示:

typedef void (^CompletionHandler)(UIBackgroundFetchResult BFR);

还要像这样定义完成处理程序属性:

@property (copy) CompletionHandler completionHandler;

然后在.m文件中设置如下:

self.completionHandler = handler;

此处需要(copy)关键字,以便复制通过startParsingWithCompletionHandler2:方法传递的块,然后由主视图控制器保留该副本。

来自文档:

  

注意:您应该将copy指定为属性属性,因为需要复制块以跟踪其在原始范围之外的捕获状态。这不是您在使用自动引用计数时需要担心的事情,因为它会自动发生,但是属性属性的最佳实践是显示结果行为。

请参阅this link

编辑:

我认为,当completionHandler(/*someArgs*/)completionHandler时,添加必须调用完成处理程序(即nil)会更好。如果这样做会导致您的应用程序崩溃。

为了解决这个问题,您可以进行简单的检查,例如

!completionHandler ?: completionHandler(/*someArgs*/);

上述代码在语义上等同于:

if (completionHandler != nil) {
    completionHandler(/*someArgs*/)
}

它只是使用三元运算符将其压缩到一行。

答案 2 :(得分:0)

类未正确初始化

使用performFetchWithCompletionHandler进行后台提取时 - 方法以不同的顺序调用,导致某些对象无法正确初始化。

当应用程序在前台启动时,这些方法被称为:(按顺序)

的initWithCoder awakeFromNib viewDidLoad中 dispatchLoadingOperation viewDidAppear

执行后台提取方法时,按以下顺序调用:

的initWithCoder awakeFromNib startParsingWithCompletionHandler2 viewDidLoad中 dispatchLoadingOperation viewDidAppear

特别需要注意的是:在dispatchLoadingOperation之前调用了vieDidLoad,它在前台运行时启动了解析。

在后台运行时,在viewDidLoad之前调用startParsingWithCompletionHandler2(在后台运行时启动解析)。

由于在viewDidLoad中初始化了几个对象,因此在预期之前开始解析,并且我的数组未初始化以存储我的解析结果。在我看来应用程序没有启动。

虽然我正在查看完成处理程序为nil的回调,但真正的问题是Class没有正确设置。