在视图控制器之间传递块

3

我为此搜索了很多,但无法找到对我的具体问题的答案。但基本上我的问题是,我是否可以将完成块传递到另一个视图控制器中,并在新视图控制器中重新定义它。

例如,在视图控制器A中,我有一个使用完成块执行下载的方法。首先,在视图控制器A中创建我的块属性。

@property (copy)void (^downloadCompleteBlock)(NSArray *downloadItems);

我试过将这个标签从复制更改为加粗,但这并没有解决我的问题。

然后我如下定义完成块。

self.downloadCompleteBlock = ^(NSArray *downloadItems) {

    NSLOG(@"download complete in view controller A";
};

然后我调用我的下载方法,并传递这个完成块。

[self download:self.downloadCompleteBlock];

然而,如果在我离开这个视图控制器时(如果下载未完成),此完成处理程序未被调用,则我希望完成块在下一个视图控制器上执行不同的操作。因此,在我的“prepare for segue”方法中,我尝试将此块传递给B视图控制器。

[controllerB setCompletionBlock:self.downloadCompleteBlock];

在视图控制器B中,这个方法重新定义了当完成块被调用时发生的事情。

- (void)setCompletionBlock:(void(^)(NSArray *downloadItems))downloadFinishedBlock {

downloadFinishedBlock = ^(NSArray *downloadItems) {

    self.collectionData = downloadItems;
    [self.collectionView reloadData];
};

然而,当下载完成时,与视图控制器B中的块相反,仍会调用视图控制器a中的原始块。有人知道如何在下载完成之前调用视图控制器B中的完成块吗?我知道我可以使用通知程序,但我很好奇是否可以使用块来实现这一点。

谢谢


关于您要做的事情,块没有什么特别之处。这与尝试设置任何其他变量完全相同;一个 int 的行为是相同的。 - jscs
1个回答

1
这是一个棘手的问题。其核心问题在于如何在第一个视图控制器消失后保留该块。您当前的代码通过让块引用self来无意中解决了这个问题。该vc由该引用保留,如果在请求完成时需要它存在,则是好消息,但现在vc和块将永远互相保留。(谷歌“保留循环”)。
那么,我们如何获得一个长时间运行的进程,在完成时运行一个块,并可能存活于两个或多个视图控制器之外?首先,将该进程拆分为自己的对象。该对象的接口将如下所示:
@interface DownloadThingy

@property (copy)void (^downloadCompleteBlock)(NSArray *);  // note, no need for dummy param names here
- (id)initWithRequestParams:(id)whateverIsNeededToStart;
- (void)start;

@end

现在,想要启动这个视图控制器的视图控制器可以声明一个强属性到它,创建一个,给它一个完成块(见下文**),并启动它。当需要进行Segue时,它可以将downloadThingy传递给另一个视图控制器,后者可以给它一个不同的完成块。
**由于请求对象被保留为一个或多个视图控制器的属性,并且由于它保留了该块,因此您仍然需要注意保留周期:(vc->downloadThingy->block->vc)
在VcA中,做如下操作:
- (void)startADownloadThingy {
    self.downloadThingy = [[DownloadThingy alloc] initWithRequestParams:someParams];
    __weak VcA *weakSelf = self;
    self.downloadThingy.downloadCompleteBlock = ^(NSArray *downloadItems) {
        // don't use self in here, use weakSelf
    }
}

VcB将在segue中被调用;它可能需要遵循相同的预防措施,也可能不需要。区别在于第二个视图控制器是否保留了downloadThingy属性。如果它不打算将其交给任何其他视图控制器,则可以跳过该属性,从而避免关于保留周期的担忧。

// another vc is handing off a running downloadThingy
- (void)heresARunningDownloadThingy:(DownloadThingy *)downloadThingy {
    // if we have our own property, then
    self.downloadThingy = downloadThingy;
    // and we need to do the weakSelf trick
    __weak VcA *weakSelf = self;
    self.downloadThingy.downloadCompleteBlock = ^(NSArray *downloadItems) {
        // don't use self in here, use weakSelf
    }
}

或者...
// another vc is handing off a running downloadThingy
- (void)heresARunningDownloadThingy:(DownloadThingy *)downloadThingy {
    // we do not have our own property
    downloadThingy.downloadCompleteBlock = ^(NSArray *downloadItems) {
        // feel free to use self in here
    }
}

最后一件事:对于DownloadThingy来说,积极地在调用完成后将其块设置为nil是一个好的做法。所以当请求完成时,请执行以下操作...

// DownloadThingy.m
// request is complete
self.downloadCompleteBlock(arrayFullOfResults);
self.downloadCompleteBlock = nil;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接