过去两个小时我一直试图解决这个问题而没有取得多大进展,我想也许是时候试着在这个问题上获得一些外界的帮助了。
基本上,我有三个NSOperations(实际上它们实际上是AFHTTPRequestOperations,但如果我理解正确使用它们作为NSOperations没有问题)我想要同时运行,并且当它们全部完成时我想运行一个第四个操作,在过程中检查错误并将其报告给用户(NSOperations当然是在NSOperationQueue中添加的。)
最后一个操作是依赖于所有这三个操作的NSBlockOperation,只有在完成三个操作时才能正确调用它。 它的结构如下
NSOperation *finishedUploading = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"Completed. Errors: %@", _errors);
_textArea.editable = YES;
if (![_errors isEqualToString: @""])
{
//Trim the last newline
_errors = [_errors substringToIndex: _errors.length-1];
dispatch_async(dispatch_get_main_queue(), ^{
//Show error message
});
} else {
dispatch_async(dispatch_get_main_queue(), ^{
//Show success dialog
});
}
}];
但是,我试图访问的_errors属性,声明为
@property NSString *errors;
在finishedUploading块中读取错误。它的值由NSLog报告为@“”,这是我在执行四个NSOperations之前设置的值。 该属性在每个操作的完成块中更改,如此
_errors = [_errors stringByAppendingString: @"Error: Test error\n"];
NSLogging _errors属性在分配后将显示正确的值,甚至检查后续执行准备NSOperationQueue的主方法的值(在将其设置回@“”之前)读取正确的值,只有finishedUploading得到错了。
此外,有时finishUploading DOES获得正确的值,但仅在第一个NSLog之后(后续的if条件被评估为true)。
我认为这是因为完成块执行得太早,实际上在执行最后一次NSOperation之前添加一秒延迟确实解决了问题,但它不是最佳解决方案,因为它增加了无用的延迟(半秒不会甚至工作)。 我一直在寻找解决方案,但即使使用@synchronized(_errors)并添加(保留)到_errors也无济于事。
Apple的文档坚持认为NSString是线程安全的,所以我不确定我做错了什么,我假设以某种方式附加到字符串引起了这个但是甚至直接设置字符串会导致问题。
修改
我已将作业更改为_errors = @"Error: Test error\n";
,以防附加字符串导致问题
我还注意到NSLogs无序到达(finishedUploading的NSLog位于阻止更改_errors值的块内的NSLog之前)
答案 0 :(得分:0)
块中的变量按值传递,因此块内的错误变量只是错误属性的副本,因此您可以在块中看到未修改原始属性的正确NSLog。
你应该做的是使用__block前缀传递给你的变量的引用。我不是块巫师,但也许这就是原因
答案 1 :(得分:0)
[_errors stringByAppendingString: @"Error: Test error\n"];
创建一个新的NSString
实例,而不是修改块中捕获的实例。使用NSMutableString
和appendString
对其进行修改,然后您将在块中获得正确的值。
答案 2 :(得分:0)
块文字制作捕获的本地状态的const副本。 _errors
变量是本地引用 - 它与封闭范围中的_errors
实例变量不同。块定义时,块中_errors
引用的值初始化,而不是块执行时。这就是为什么你没有看到更新的价值。
通常,从块中引用实例变量往往会造成混淆。如果它按照你期望的方式工作,那么从概念上来说这将代表对封装的严重违反,所以也许最好不要这样做。
在任何情况下,请考虑使用块内的访问器方法来访问封闭范围中对象的状态,而不是通过直接引用它们来获取对象的实例变量的快照。更一般地说,当使用声明的属性时,更喜欢使用属性的访问器来直接获取和设置底层实例变量;这可以帮助你避免像这样的小陷阱。
您的重写块可能如下所示:
NSOperation *finishedUploading = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"Completed. Errors: %@", self.errors);
self.textArea.editable = YES;
if (![self.errors isEqualToString:@""])
{
//Trim the last newline
self.errors = [self.errors substringToIndex:self.errors.length-1];
dispatch_async(dispatch_get_main_queue(), ^{
//Show error message
});
} else {
dispatch_async(dispatch_get_main_queue(), ^{
//Show success dialog
});
}
}];
请注意,该块正在捕获self
(直接访问ivar时也是如此),因此您可能正在创建保留周期。为避免这种情况,请参阅有关打破块保留周期的SO问题,例如:Weak references in blocks and retain cycles