在回调中通过引用传递

时间:2015-01-16 10:33:23

标签: ios objective-c callback objective-c-blocks pass-by-reference

我遇到了问题。我将一个对象通过它的引用传递给另一个类。设置该对象中的值。现在,当我在回调处理程序中访问此变量时,它就是nil。

我的示例代码是:

A类:

__block NSString *getListJobId = nil;
ClassB *bobject = [[ClassB alloc]init];
    [bobject getItemsWithJobId:&getListJobId onSuccess:^(NSArray *response) {
        NSLog(@"job id %@",getListJobId); //It is nil, It should be **shiv**
    } onFailure:^(NSError *error) {
    }];

B类: ·H

- (void)getItemsWithJobId:(NSString **)jobId onSuccess:(void (^)(NSArray *))completedBlock onFailure:(void (^)(NSError *))failureBlock;

的.m

- (void)getItemsWithJobId:(NSString **)jobId onSuccess:(void (^)(NSArray *))completedBlock onFailure:(void (^)(NSError *))failureBlock
{
    *jobId = @"shiv";
    completedBlock([NSArray new]);
}

我在回调响应中的A类中得到 jobId nil 。如何从B类到A类获得此值。

感谢您的帮助。

2 个答案:

答案 0 :(得分:1)

您不应该通过引用传递以获取方法中的更新值,因为ClassA和ClassB中的getListJobId不指向相同的地址。

Obj-C块捕获其封闭范围之外的变量值。 请参阅“块可以从封闭范围中捕获值”部分。

https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html

我们可以从块的参数中获取更新的值,并在块中更新getListJobId,而不是通过引用传递。

A类:

__block NSString *getListJobId = nil;
ClassB *bobject = [[ClassB alloc] init];
[bobject getItemsWithJobId:getListJobId onSuccess:^(NSArray *response, NSString *updatedJobId) {
    getListJobId = updatedJobId;
    NSLog(@"job id %@", getListJobId); // job id **shiv**
} onFailure:^(NSError *error) {
}];

B类:.h

- (void)getItemsWithJobId:(NSString *)jobId onSuccess:(void (^)(NSArray *, NSString *))completedBlock onFailure:(void (^)(NSError *))failureBlock;

<强>的.m

- (void)getItemsWithJobId:(NSString *)jobId onSuccess:(void (^)(NSArray *, NSString *))completedBlock onFailure:(void (^)(NSError *))failureBlock
{
    NSString *updatedJobId = @"**shiv**";
    completedBlock([NSArray new], updatedJobId);
}

答案 1 :(得分:0)

获取__block变量的地址并不总是符合您的预期。

在当前实现中,__block变量最初在堆栈上分配,然后在将使用它的任何块移动到堆中时“移动”到堆(这是由块所引起的)复制)。

因此, __block变量的地址会在其生命周期内发生变化。如果您获取它的地址并移动,那么您将不再指向其他人正在使用的变量版本。

在这里,发生的事情是,当__block变量getListJobId仍在堆栈中时,它会获取该地址。它仍然在堆栈上,因为它通过复制使用它的任何块而被移动到堆,但是还没有创建块。

然后,使用getListJobId的块被复制到某处,getListJobId被移动到堆中。究竟发生这种情况的地方并不十分清楚,因为允许ARC在不同的地方插入块的副本。另外,你在这里显示的代码看起来不像你的真实代码,因为没有必要同步在方法结束时调用“完成块”(在这种情况下你只需返回并让调用者执行完成后他们想要的操作)。相反,您的实际代码可能执行异步操作,最后调用完成处理程序。 dispatch_async和相关的异步函数复制传递给它们的块(它们会复制捕获的任何块,依此类推)。

我猜你的实际代码中,*jobId = @"shiv";行和完成块的调用都发生在异步操作中。发生的事情是异步操作的创建会复制块并导致getListJobId移动到堆中。所以在异步操作中,getListJobId指的是变量的堆版本。但是,*jobId = @"shiv";写入变量的堆栈版本,因为jobId是从变量的地址中取出的指针,当变量仍在堆栈中时。所以你要写和阅读不同的变量。

此外,您在*jobId = @"shiv";中所做的事情非常危险,因为在异步操作时,原始函数调用的堆栈帧不再存在。堆栈帧消失后写入堆栈中的变量是未定义的行为,您可能会覆盖内存中的其他未知变量。你很幸运它没有崩溃。