Objective c 绑定到块的目标C间接指针无法更新直接指针

Objective c 绑定到块的目标C间接指针无法更新直接指针,objective-c,objective-c-blocks,objective-c-runtime,Objective C,Objective C Blocks,Objective C Runtime,我有一个函数,它将一个间接指针绑定到一个块中,并将块返回给直接指针,以便以后赋值,如下所示: @interface SomeClass : NSObject @property int anInt; @end @implementation SomeClass @end typedef void(^CallbackType)(int a); - (CallbackType)getCallbackToAssignTo:(SomeClass **)indirectPointer { retu

我有一个函数,它将一个间接指针绑定到一个块中,并将块返回给直接指针,以便以后赋值,如下所示:

@interface SomeClass : NSObject
@property int anInt;
@end
@implementation SomeClass
@end

typedef void(^CallbackType)(int a);

- (CallbackType)getCallbackToAssignTo:(SomeClass **)indirectPointer {
  return ^(int a){
    NSLog(@"indirectPointer is: %p", indirectPointer);
    NSLog(@"*indirectPointer is: %p", *indirectPointer);
    (*indirectPointer) = [[SomeClass alloc] init];
    (*indirectPointer).anInt = a;
    NSLog(@"After: indirectPointer is: %p", indirectPointer);
    NSLog(@"After: *indirectPointer is: %p", *indirectPointer);
  };
}

- (void)iWillNotDoWhatImSupposedTo {
  SomeClass *directPointer = nil;
  CallbackType cb = [self getCallbackToAssignTo:(&directPointer)];
  NSLog(@"directPointer is pointing to: %p", directPointer);
  NSLog(@"&directPointer is pointing to: %p", &directPointer);
  cb(1);
  NSLog(@"after callback directPointer is: %p", directPointer);
  NSLog(@"after callback &directPointer is: %p", &directPointer);
}
SomeClass * __autoreleasing temporaryPointer = directPointer;
CallbackType cb = [self getCallbackToAssignTo:&temporaryPointer];
directPointer = temporaryPointer;
问题是,虽然这一切都在编译和运行,但当块返回时,块的操作会立即被忘记。运行[IwillnotdowhatimAssociatedTo]的打印输出为:

directPointer is pointing to: 0x0
&directPointer is pointing to: 0x7fff5ce1d060
--- callback execution starts here
indirectPointer is pointing to: 0x7fff5ce1d050
*indirectPointer is pointing to: 0x0
After assignment: indirectPointer is pointing to: 0x7fff5ce1d050
After assignment: *indirectPointer is pointing to: 0x61800001e1d0
--- callback returns here, and the contents of the pointer is lost
after running callback directPointer is pointing to: 0x0
after running callback &directPointer is pointing to: 0x7fff5ce1d060

关于如何使这个回调工作,您有什么见解吗?

我认为您必须从块外部对新创建的对象有一个很强的引用。您可以通过holder类实现这一点,例如:

@inferface Holder: NSObject
@property (strong) id object;
@end
@implementation Holder
@end

- (CallbackType)getCallbackToAssignTo:(Holder *)inHolder {
    return ^(int a){
        inHolder.object = [[SomeClass alloc] init];
        [inHolder.object setAnInt:a];
    };
}

- (void)thisShouldWork {
    Holder *theHolder = [Holder new];
    CallbackType cb = [self getCallbackToAssignTo:theHolder];

    cb(1);
    SomeClass *directPointer = theHolder.object;
}

问题实际上是论点的自动释放。实际上,方法签名:

- (CallbackType)getCallbackToAssignTo:(SomeClass **)indirectPointer;
实际上相当于:

- (CallbackType)getCallbackToAssignTo:(SomeClass * __autoreleasing *)indirectPointer;
__自动删除用于表示通过引用(id*)传递并在返回时自动删除的参数

一种解决方案是将_; strong指定为生存期限定符

- (CallbackType)getCallbackToAssignTo:(SomeClass * __strong *)indirectPointer;

更重要的参考:

Idali是正确的,因为它与以下事实有关:参数是“指向自动释放的指针”,而您正在传递“指向强的指针”。但是理解为什么这在这里是相关的很重要(因为在大多数情况下它是不相关的),它与引用计数本身无关

您正在将一个“指向强的指针”(
directPointer
是一个
SomeClass*\uu strong
,因此
&directPointer
是一个
SomeClass*\uu strong*
)作为参数传递给一个“指向自动删除的指针”的参数。这是怎么回事?ARC通过一种称为“”的技术来处理此问题

基本上,它所做的是创建一个“autoreleasing”类型的临时变量,将强指针指定给它(如果该值不为null),然后使用指向该临时变量的指针调用该方法。然后在方法返回后,它将变量的值赋回到强变量中;大致如下:

@interface SomeClass : NSObject
@property int anInt;
@end
@implementation SomeClass
@end

typedef void(^CallbackType)(int a);

- (CallbackType)getCallbackToAssignTo:(SomeClass **)indirectPointer {
  return ^(int a){
    NSLog(@"indirectPointer is: %p", indirectPointer);
    NSLog(@"*indirectPointer is: %p", *indirectPointer);
    (*indirectPointer) = [[SomeClass alloc] init];
    (*indirectPointer).anInt = a;
    NSLog(@"After: indirectPointer is: %p", indirectPointer);
    NSLog(@"After: *indirectPointer is: %p", *indirectPointer);
  };
}

- (void)iWillNotDoWhatImSupposedTo {
  SomeClass *directPointer = nil;
  CallbackType cb = [self getCallbackToAssignTo:(&directPointer)];
  NSLog(@"directPointer is pointing to: %p", directPointer);
  NSLog(@"&directPointer is pointing to: %p", &directPointer);
  cb(1);
  NSLog(@"after callback directPointer is: %p", directPointer);
  NSLog(@"after callback &directPointer is: %p", &directPointer);
}
SomeClass * __autoreleasing temporaryPointer = directPointer;
CallbackType cb = [self getCallbackToAssignTo:&temporaryPointer];
directPointer = temporaryPointer;
请注意,如果
-getCallbackToAssignTo:
方法仅在其自身执行中同步使用参数,则
SomeClass*\uuu strong*
参数和
SomeClass*\uu自动删除*
参数之间没有区别,因为pass-by-writeback机制确保在方法执行期间所做的任何更改都能正确地反映在您最初试图传递指针的强变量中

这里的问题是,您的
-getCallbackToAssignTo:
方法将指向指针的指针存储到一个数据结构(一个块)中,该数据结构(一个块)比方法的执行时间长(该块被返回),这允许一些代码(块的主体)稍后使用存储的指针尝试修改它指向的指针。但指针指向的是临时变量,该变量在方法执行后不再使用。因此,修改临时变量并不反映在原始变量上


更糟糕的是,它基本上是未定义的行为。编译器发现,在通过写回方案传递后,临时变量不再使用,并且可能允许将该变量的内存重新用于其他用途。您拥有的是一个悬空指针,指向编译器不希望您继续使用的变量。将参数类型从“指向自动释放的指针”更改为“指向强指针”并不能真正解决此问题--尽管这将消除通过写回的情况,这可能会修复这种直接情况,事实上,块可以从指向的变量范围之外返回或存储在可以使用的位置,这仍然会导致使用悬空指针。基本上,在存储或使用块捕获常规C指针时必须非常小心,因为该块无法确保所指向变量的生存期(与块捕获Objective-C对象指针不同,在这种情况下它会保留该指针)。

不确定这是如何发生的,但我觉得有趣的是,直接指针所在的地址与间接指针所在的地址不同(分别以60结尾,然后是50结尾),可能在您的实际代码中,您正在传递要设置的ivar地址?否则,您应该让块直接返回新实例。