【问题标题】:Pass by Reference in Callbacks在回调中通过引用传递
【发布时间】:2015-01-16 10:33:22
【问题描述】:

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

我的示例代码是:

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 类获取此值。

感谢您的帮助。

【问题讨论】:

  • 尝试将getListJobId 初始化为NULL而不是nil
  • @gabuh:它不工作.. :(

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


【解决方案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);
}

【讨论】:

    【解决方案2】:

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

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

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

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

    然后,使用getListJobId 的块被复制到某处,getListJobId 被移动到堆中。具体发生在哪里还不是很清楚,因为允许 ARC 在不同的地方插入块的副本。另外,您在此处显示的代码看起来不像您的真实代码,因为在方法末尾同步调用“完成块”是没有意义的(在这种情况下,您只需返回并让调用者执行完成后他们想要的操作)。相反,您的真实代码可能会执行一个异步操作,最后调用完成处理程序。 dispatch_async 和相关的异步函数复制传递给它们的块(进而复制捕获的任何块,等等)。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-09
      • 1970-01-01
      • 1970-01-01
      • 2019-01-09
      • 1970-01-01
      • 2013-11-05
      • 2011-01-14
      • 2015-01-15
      相关资源
      最近更新 更多