重构代码后“缺少__block类型说明符”编译错误,但在重构之前确定

我有一些代码可以下载图像并将其分配到一个块中。 代码当前有效,但是我想将它重构为一个单独的方法,但是在重构后我得到了一个编译错误。

这是原始代码,它编译并运行下载的图像成功分配:

- (void) someMethod { … MyObject* object = [[MyObject alloc] init]; [self.objects addObject: object]; NSString* graphRequest = [NSString stringWithFormat:@"%@%@%@", @"https://graph.facebook.com/", fbid, @"/picture?type=square"]; FBRequest *fbRequest = [FBRequest requestForGraphPath: graphRequest]; [fbRequest startWithCompletionHandler: ^(FBRequestConnection *connection, id result, NSError *theError) { NSDictionary *dict = (NSDictionary *) result; if (dict) { NSString* urlAsString = [dict objectForKey:@"id"]; if ([urlAsString length] > 0) { NSURL *url = [NSURL URLWithString: urlAsString]; NSData *data = [NSData dataWithContentsOfURL:url]; object.image = [UIImage imageWithData:data]; } } }]; } 

如果我将其重构为以下内容,则会出现编译错误

 - (void) someMethod { … MyObject* object = [[MyObject alloc] init]; [self.objects addObject: object]; [self fetchFbImage: object.image forFbid:fbid]; } - (void) fetchFbImage:(UIImage*) targetImage forFbid:(NSString*) fbid { NSString* graphRequest = [NSString stringWithFormat:@"%@%@%@", @"https://graph.facebook.com/", fbid, @"/picture?type=square"]; FBRequest *fbRequest = [FBRequest requestForGraphPath: graphRequest]; [fbRequest startWithCompletionHandler: ^(FBRequestConnection *connection, id result, NSError *theError) { NSDictionary *dict = (NSDictionary *) result; if (dict) { NSString* urlAsString = [dict objectForKey:@"id"]; if ([urlAsString length] > 0) { NSURL *url = [NSURL URLWithString: urlAsString]; NSData *data = [NSData dataWithContentsOfURL:url]; targetImage = [UIImage imageWithData:data]; } } }]; } 

编译错误是分配给targetImage的行,“变量不可分配(缺少__block类型说明符)”。

我应该在哪里添加__block类型说明符? 为什么在重构之后会出现问题而不是之前的问题?

谢谢

您似乎对参数如何在Objective-C中工作感到困惑。

在原始代码中,您有:

 MyObject* object = [[MyObject alloc] init]; 

它将object声明为方法的局部变量。 然后在你写的块内:

 object.image = [UIImage imageWithData:data]; 

因此,您的块引用局部变量object并且(因为声明中没有__block属性)该块获取变量内容的常量副本。 这很好,因为你没有改变object ,它是对MyObject实例的引用,但是在该实例上调用一个方法来改变该实例的内部状态。[*]

现在让我们来看看你的重构。 你删除了someMethod代码的一大块,并将它放在一个新方法fetchFbImage 。 在someMethod您调用fetchFbImage

 [self fetchFbImage:object.image forFbid:fbid]; 

这会将object.image当前值 object.image给它传递属性的方法,以便可以在fetchFbImage其赋值。 参数targetImage的类型是UIImage * – 对UIImage的引用 – 它不是“ UIImage *类型的属性” – 你不能拥有这种类型的参数,属性不能只传递它们的值或引用对象有财产。

当一个方法被调用时,每个参数实际上是一个局部变量,它被初始化为传递的参数值,所以上面的方法调用有效地执行:

 UIImage *targetImage = object.image; 

其中targetImagefetchFbImage范围内的局部变量, objectsomeMethod范围内的局部变量。

现在在fetchFbImage的块内你写:

 targetImage = [UIImage imageWithData:data]; 

这有两个原因:

  1. 您无法在块中分配targetImage 。 这是属于fetchFbImage的局部变量,该局部变量不归属于__block ,因此该块具有它的常量副本 – 并且您不能分配给常量。 这就是编译器发出错误消息的原因。
  2. 但是你的问题比这个大,你假设对fetchFbImage的局部变量targetImage将会有一些如何调用属性object.image – 并且它无法做到这一点。 targetImage只是一个局部变量,它通过方法调用初始化为object.image

解决这个问题的唯一方法是将fetchFbImage传递给你的MyObject实例,然后传递给fetchFbImage里面的块,以便像预先重构的代码一样分配给该对象的image属性。

所以你的代码看起来像:

 - (void) fetchFbImage:(MyObject *)targetObject forFbid:(NSString *)fbid { ... targetObject.image = [UIImage imageWithData:data]; ... } ... [self fetchFbImage:object forFbid:fbid]; 

HTH

附录

看到您对另一个答案的评论,您希望fetchFbImage不了解MyObject并且能够获取图像,无论它们将从哪里引用。

一种简单的方法是遵循FBRequest的相同设计 – 使用完成处理程序。 为方便起见,定义完成块的类型:

 typedef void (^ImageConsumer)(NSImage *image); 

现在定义您的方法以采取以下方法之一:

 - (void) fetchFbImageForFbid:(NSString *)fbid completionHandler:(ImageConsumer)handler 

fetchFbImageForFbid的块中,将图像传递给处理程序:

 handler([UIImage imageWithData:data]); 

someMethod的调用中传递一个块,该块将值赋给您的属性:

 [self fetchFbImageForFbid:fbid completionHandler:^(NSImage *image) { object.image = image; } ]; 

[*]如果这令人困惑,请将参考文献视为房屋的地址。 地址是不变的,房子里有多少人不是。 你可以告诉别人“去这个地址,有一个伟大的聚会” – 房子的内容(“状态”)改变,它的地址没有。

在你的第一个方法(没有重构),你可以将图像设置为对象,因为你已经引用了对象,所以你做了

 object.image = [UIImage imageWithData:data]; 

但是在重构之后,你无法以你的方式设置图像,你也应该发送对象。

 - (void) someMethod { … MyObject* object = [[MyObject alloc] init]; [self.objects addObject: object]; [self fetchFbImageForObj:object forFbid:fbid]; } - (void) fetchFbImageForObject:(MyObject*)object forFbid:(NSString*) fbid { NSString* graphRequest = [NSString stringWithFormat:@"%@%@%@", @"https://graph.facebook.com/", fbid, @"/picture?type=square"]; FBRequest *fbRequest = [FBRequest requestForGraphPath: graphRequest]; [fbRequest startWithCompletionHandler: ^(FBRequestConnection *connection, id result, NSError *theError) { NSDictionary *dict = (NSDictionary *) result; if (dict) { NSString* urlAsString = [dict objectForKey:@"id"]; if ([urlAsString length] > 0) { NSURL *url = [NSURL URLWithString: urlAsString]; NSData *data = [NSData dataWithContentsOfURL:url]; object.image = [UIImage imageWithData:data]; } } }]; } 

试试吧。如果有任何错误或其他什么,请在下面评论。