2

所以我有:

@interface testAppControl : NSObject
{
    NSString *s;
}

然后在我的街区我想做

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

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

所以我想"something"在我离开我的街区后保持这个价值。ARC有可能吗?

4

4 回答 4

3

的存在completionHandler可能会导致人们从逻辑上推断这setGroupWithName是一种异步方法。这是 iOS 开发中常见的编程模式:与其在前台执行一些可能耗时的过程(在此期间用户界面将被冻结),不如在后台异步执行,但传递一个 blockcompletionHandler在这种情况下,所以您可以确定异步过程完成后应该发生什么。

在这种情况下,您似乎正在setGroupWithName某个后台线程/队列上调用 ,并理解在发生这种情况时,您将继续在主线程上(在您的情况下,在该NSLog语句中),确保您的应用程序保持响应而那个缓慢的操作正在完成。但是当那个异步后台操作setGroupWithName完成时,它将执行由代表的代码块completionHandler(在你的情况下,设置sto @"something")。

如果你在你设置为NSLog的块内放置一个语句,你可能会看到它在调用.completionHandlers@"something"NSLogsetGroupWithName


为了说明这个例子,这里是一个标准 Cocoa 方法的例子,它有一个completionHandler参数,即NSURLConnection类方法,sendAsynchronousRequest.

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

因此,请考虑以下代码:

- (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语句的时间戳说明了所有这些的时间安排:

2013-05-02 20:42:58.922 myapp[81642:c07]-[ViewController performSearch:] 开始:数组有 0 个项目
2013-05-02 20:42:58.923 myapp[81642:c07] -[ViewController performSearch:] 结束:数组仍有 0 个项目
2013-05-02 20:42:59.798 myapp[81642:1303] __32-[ViewController performSearch:]_block_invoke:后台查询后,数组现在有 11 个项目

请注意,“开始”和“结束”之间经过的时间大约为 1 毫秒,但在后台完成 Twitter 搜索几乎需要整整一秒。设计原则是,如果我们不异步执行此操作,completionHandler在这种情况下使用 ,应用程序的用户界面将在那一秒钟内被冻结。但是通过使用异步编程技术,该应用程序仍然很好并且响应迅速,而是在后台执行缓慢的操作。

我不熟悉您的setGroupWithName方法,但它可能正在执行相同类型的异步操作。因此,您尝试在之后立即查看该值不会反映该方法完成后将更改的值(有点像我示例中的“结束”NSLog 语句不反映将发生的更改的事实)秒后)。

于 2013-05-02T23:32:38.837 回答
3

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

__block NSString *s = nil;

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

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

block();
NSLog(@"%@", s);
于 2013-05-02T21:02:13.893 回答
0

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

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

我建议使用一个属性,并在块中捕获一个“弱自我”指针,以避免在实例持有s被释放的情况下出现错误的内存访问。

于 2013-05-02T21:02:02.750 回答
0

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

  • 在块外声明变量s,它会自动复制到块内
  • 如果您需要更改它的值并读取它并使用说明 __block符在块外声明它,请注意,如果您在异步进程中更改该 var,则块后面的值很可能是无用的
  • 如果您想在每次调用块期间保留和更改其值,请将其贴在块内,因为static放置它将仅在块内“可见”
于 2013-05-02T20:57:16.387 回答