我怎样才能保留一个块中设置的局部variables?
所以我有:
@interface testAppControl : NSObject { NSString *s; }
然后在我的方块我想要做的
[SendAPI setGroupWithName:groupName completionHandler:^(NSArray *errors) { s = @"something"; }]; NSLog(@"%@", s); //<--- s is null
所以我想在离开我的街区之后保留价值"something"
。 这可能与ARC?
将其声明为__block
variables,然后可以在块外部使用它,并在块内部对其进行更改。
__block NSString *s = nil; void(^block)(void) = ^ { s = @"something"; }; block(); NSLog(@"%@", s); s = @"blah"; NSLog(@"%@", s); block(); NSLog(@"%@", s);
completionHandler
存在可能导致逻辑上推断出setGroupWithName
是一个asynchronous方法。 这是iOS开发中的一种常见的编程模式:在前台执行一些耗时的过程(在此期间用户界面将被冻结),而不是在后台asynchronous执行它,而是传递一个块 ,在这种情况下为completionHandler
,所以您可以确定asynchronous过程完成后应该发生什么。
在这种情况下,它看起来像你调用setGroupWithName
,在一些后台线程/队列,理解,发生这种情况,你会继续在主线程(在你的情况下,在NSLog
语句),确保你的应用程序保持响应,而缓慢的操作正在完成。 但是,当setGroupWithName
asynchronous后台操作完成时,它将执行由completionHandler
表示的代码块(在你的情况下,将s
设置为@"something"
)。
如果你在completionHandler
块中放置了一个NSLog
语句,那么你可能会发现它在调用setGroupWithName
之后的现有NSLog
语句之后发生了setGroupWithName
。
为了说明这个例子,下面是一个带有completionHandler
参数的标准Cocoa方法的例子,也就是NSURLConnection
类的方法sendAsynchronousRequest
。
问题是如何执行一个缓慢的操作,但仍然有一个响应的用户界面,而不必等待互联网的慢操作。
所以,请考虑以下代码:
- (void)performSearch:(NSString *)term { NSLog(@"%s start: array has %d items", __FUNCTION__, [self.array count]); // prepare to do search (the details here are not relevant) NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://search.twitter.com/search.json?q=%@", term]]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; // submit an Internet search that will be performed in the background [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { // in the background, when the Twitter search is done, // and when it's done, we'll make an array of the results self.array = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; NSLog(@"%s: after background query, array now has %d items", __FUNCTION__, [self.array count]); }]; // in the meantime, let's immediately carry on while that search is taking place NSLog(@"%s end: array still has %d items", __FUNCTION__, [self.array count]); }
这段代码的细节不是很重要,但基本思想是它做的很慢(在因特网上执行Twittersearch),但是它会立即继续在主线程中运行代码,让后台线程继续自己的步伐。
各种NSLog
语句的时间戳说明了所有这些的时间:
2013-05-02 20:42:58.922 myapp [81642:c07] - [ViewController performSearch:] start:array has 0 items 2013-05-02 20:42:58.923 myapp [81642:c07] - [ViewController performSearch:] end:数组仍然有0个项目 2013-05-02 20:42:59.798 myapp [81642:1303] __32- [ViewController performSearch:] _ block_invoke:后台查询后,数组现在有11个项目
请注意,“开始”和“结束”之间的时间大约为1毫秒,但是在后台完成Twittersearch需要几乎整整一秒的时间。 devise原则是,如果我们不这样做,在这种情况下使用completionHandler
,应用程序的用户界面将被冻结一秒钟。 但通过使用asynchronous编程技术,该应用程序保持良好和响应,而是在后台执行缓慢的操作。
我不熟悉你的setGroupWithName
方法,但它大概是执行相同types的asynchronous操作。 因此,你试图看后面的值不会反映一旦完成该方法将会改变的值(有点像我的例子中的“结束”NSLog语句不反映将发生的变化秒以后)。
不完全清楚你为什么想要做什么,所以我给你3个select:
- 在块外部声明variables
s
,它将自动在块内复制 - 如果你需要改变它的值并读取它在
__block
说明符的块之外声明它,请注意,如果你在asynchronous过程中改变这个var,块后面的值很可能是无用的 - 如果要在每次调用块时将其值保持并更改,请将其作为
static
块放入块中,仅在块内“可见”
当你正在执行一个asynchronous操作,并且在完成块被调用的时候,你已经离开了定义s
的函数/范围。
考虑在一个“全局”范围内(如类variables或属性)声明s
,那么只要你的类实例处于活动状态,你将能够读取块设置的结果。
我build议使用一个属性,并在块中捕获一个“弱自我”指针,以避免在持有s
的实例被重新分配的情况下访问错误的内存。