如何保留块中设置的局部变量?

时间:2013-05-02 20:50:06

标签: ios objective-c xcode objective-c-blocks

所以我有:

@interface testAppControl : NSObject
{
    NSString *s;
}

然后在我的块中我想做

[SendAPI setGroupWithName:groupName completionHandler:^(NSArray *errors) {
    s = @"something";
}];

NSLog(@"%@", s); //<--- s is null

所以我想在离开块后保留值"something"。这可能与ARC有关吗?

4 个答案:

答案 0 :(得分:3)

将其声明为__block变量,然后您可以在块外使用它并在块内更改它。

__block NSString *s = nil;

void(^block)(void) = ^ {
    s = @"something";
};
block();
NSLog(@"%@", s);

s = @"blah";
NSLog(@"%@", s);

block();
NSLog(@"%@", s);

答案 1 :(得分:3)

completionHandler的出现可能导致人们逻辑推断setGroupWithName是一种异步方法。这是iOS开发中常见的编程模式:不是在前台执行一些可能耗时的过程(在此期间用户界面将被冻结),而是在后台异步执行,但是pass it a block,{{1}在这种情况下,您可以确定异步过程完成时应该发生什么。

在这种情况下,看起来你正在某个后台线程/队列上调用completionHandler,并且理解当发生这种情况时,你将继续主线程(在你的情况下,在那个{ {1}}语句),确保您的应用程序在缓慢的操作完成时保持响应。但是当setGroupWithName的异步后台操作完成后,它将执行NSLog所代表的代码块(在您的情况下,setGroupWithNamecompletionHandler的设置)

如果您在s区块中放置@"something"语句,您将NSLog设置为completionHandler,您可能会发现它发生得很好在调用s之后的现有@"something"语句之后。


为了举例说明,下面是一个标准Cocoa方法的示例,该方法具有NSLog参数,即setGroupWithName类方法sendAsynchronousRequest

问题是如何执行慢速操作,但仍然具有响应的用户界面,而无需等待慢速的Internet操作。

因此,请考虑以下代码:

completionHandler

这段代码的细节不是很重要,但基本的想法是它做的很慢(在互联网上执行Twitter搜索),但它会立即继续在主线程中运行代码,让后台线程继续按照自己的节奏。

各种NSURLConnection语句的时间戳说明了所有这些的时间:

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: array still has 0 items
2013-05-02 20:42:59.798 myapp[81642:1303] __32-[ViewController performSearch:]_block_invoke: after background query, array now has 11 items

请注意,“开始”和“结束”之间经过的时间大约是1毫秒,但在后台完成Twitter搜索几乎需要一秒钟才能完成。设计原则是,如果我们不是异步执行此操作,在这种情况下使用- (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]); } ,应用程序的用户界面将被冻结一秒钟。但是通过使用异步编程技术,应用程序仍然很好并且响应迅速,而是在后台执行慢速操作。

我不熟悉你的NSLog方法,但它可能会执行相同类型的异步操作。因此,您尝试立即查看该值后的值将不会反映该方法完成后将更改的值(类似于我的示例中的“end”NSLog语句未反映将发生的更改的事实第二次)。

答案 2 :(得分:0)

不完全清楚为什么以及你想做什么,所以我给你3个选择:

  • 在块外声明变量s,它将被复制到内部 自动在块内
  • 如果您需要更改其值并阅读它,请在块外面声明它 <{1}}说明符,请注意,如果要在异步进程中更改该var,则块之后的值很可能无用
  • 如果您想在每次调用期间保留并更改其值 阻止它在块内部进行decalre __block使其仅在块内“可见”

答案 3 :(得分:0)

当您执行异步操作时,当调用完成块时,您已经离开了定义s的函数/范围。

考虑在“全局”范围内声明s(如类变量或属性),只要您的类实例处于活动状态,您就可以读取该块设置的结果。

我建议使用一个属性,并在块中捕获一个“弱自我”指针,以避免在保存s的实例被解除分配的情况下访问内存不良。