我有一些代码可以下载图像并将其分配到一个块中。代码当前有效,但是我想将它重构为一个单独的方法,但是在重构后我得到了一个编译错误。
这是编译并运行下载图像成功分配的原始代码:
- (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<FBGraphObject> *dict = (NSDictionary<FBGraphObject> *) 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<FBGraphObject> *dict = (NSDictionary<FBGraphObject> *) 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类型说明符?为什么在重构之后会出现问题但不是之前的问题?
由于
答案 0 :(得分:4)
您似乎对参数在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
这会将fetchFbImage
的当前值传递给不传递属性的方法,以便可以将其分配到{{} 1}}。参数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; }
];
[*]如果这令人困惑,请将参考文献视为房屋的地址。地址是不变的,房子里有多少人不是。你可以告诉某人“去这个地址,有一个伟大的聚会” - 房子的内容(“状态”)改变,它的地址没有。
答案 1 :(得分:1)
在你的第一个方法中(没有重构),你可以将图像设置为对象,因为你引用了对象,所以你做了
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<FBGraphObject> *dict = (NSDictionary<FBGraphObject> *) 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];
}
}
}];
}
试一试。如果有任何错误或其他原因,请在下方发表评论。