重构代码后“缺少__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;
其中targetImage
是fetchFbImage
范围内的局部变量, object
是someMethod
范围内的局部变量。
现在在fetchFbImage
的块内你写:
targetImage = [UIImage imageWithData:data];
这有两个原因:
- 您无法在块中分配
targetImage
。 这是属于fetchFbImage
的局部变量,该局部变量不归属于__block
,因此该块具有它的常量副本 – 并且您不能分配给常量。 这就是编译器发出错误消息的原因。 - 但是你的问题比这个大,你假设对
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]; } } }]; }
试试吧。如果有任何错误或其他什么,请在下面评论。