【问题标题】:Double pointer as Objective-C block parameter双指针作为 Objective-C 块参数
【发布时间】:2013-10-21 21:20:29
【问题描述】:

是否可以(如果可以,安全)创建/使用将双指针作为参数的块?

例如:

- (void)methodWithBlock:(void (^)(NSError **error))block;


其他背景、研究和问题:

  • 我正在使用 ARC。
  • 当我声明上面的方法并尝试调用它时,XCode 自动完成我的方法调用如下:[self methodWithBlock:^(NSError *__autoreleasing *error) {}]; __autoreleasing 在这里是什么意思,为什么要添加它?我认为这与 ARC 有关。
  • 如果这个可能且安全的,指针是否仍然可以像在其他任何地方一样在块中被取消引用?
  • 一般而言,执行我所描述的操作与简单地将双指针作为方法参数(例如- (void)methodWithDoublePointer:(NSError **)error;)传递之间的重要区别是什么?应该考虑哪些特殊因素(如果有的话)(再次假设这是可能的)?

【问题讨论】:

  • @verec 不是每个人都在使用 ARC...
  • 但大多数人都这样做! :-)
  • 是的...让我们隐藏计算机编程中最重要的部分之一,所以感觉就像魔术,就像你不应该关心,就像你不应该担心...... ^^
  • @H2CO3,值得深入阅读该线程。 Rincewind 在这里总结得很好:devforums.apple.com/message/906963#906963。 ARC 比手工编码更好地解决了绝大多数问题,但每隔一段时间,您就需要对其进行调整。并不是说它比编译器更隐藏了编程的重要部分。我可以比 clang 更好地在 NEON 中手动编写一些循环(我已经这样做了)。这并不意味着我应该使用 -O0 进行编译,所以“我完全可以控制”。那将是愚蠢的。在绝大多数情况下,关闭 ARC 也是如此。
  • gparker 还指出了与 ARC (devforums.apple.com/message/904969#904969) 的一个经常被忽视的差异。 ARC首先关注安全;然后是性能。 (没有程序比崩溃更慢。) ARC 假设您可能会在您的方法中做一些奇怪的事情,并通过一些人们并不总是手动包含的额外保留来防止这种情况发生。在一些紧密的循环中,这可能是一个问题(我还看到很多应用程序崩溃的情况,因为人们没有把它们放进去)。对于高性能循环,我通常使用 C,但这并不意味着我大部分时间都避免使用 ObjC。

标签: objective-c objective-c-blocks


【解决方案1】:

是的,指针始终只是指针。您只需要确保在发送消息之前取消引用它(假设为 objc 对象)。

还要注意指针可能为零。在尝试取消引用它或你有什么之前,请务必检查它。

正如@verec 提到的,如果您使用 ARC,您应该将参数声明为 __autoreleasing

根据the docs

__autoreleasing 用于表示通过引用 (id *) 传递并在返回时自动释放的参数。

记住 id 是一个指向对象的指针,所以说的是对象**

将指针传递给指向方法或块的指针没有区别。

【讨论】:

  • 其实你不必指定__autoreleasing,编译器假定在这种情况下默认情况下。
  • 即使它假设它用于传递地址的 NSError* 也应根据 apple docs 中的示例声明为 __autoreleasing
【解决方案2】:

答案是“是”和“否”……

在基本级别上,将指针传递给块的指针与将它们传递给方法没有什么不同;而且,通常的条件是你的指针必须是有效的,这是完全可以的。

然而__autoreleasing 在这里非常意义重大,并且与 ARC 和 pass-by-writeback 相关。使用该块是否会按预期工作将取决于上下文,因为编译器在传递 NSError * __autoreleasing * 类型的参数时经常使用隐藏变量作为 pass-by-writeback 实现的一部分。

如果 pass-by-writeback 不是您需要的,或者不适合,您可能希望声明您的块采用不同的类型,例如 NSError * __strong *。阅读this answer,它解释了幕后发生的事情,这应该可以帮助您确定在您的上下文中块声明是否是好的。

总而言之,(a) 声明块是可以的,但是 (b) 你需要了解它是如何被调用的,并且可能需要更改签名。

【讨论】:

    【解决方案3】:

    警告:未经测试

    为了论证起见,让我们开始:

    typedef NSError * NSErrorPtr ;
    
    - (void) foo: (NSErrorPtr *) errPtr {
        *errorPtr = [NSError new] ;
    }
    

    errPtr 既没有被声明为 __weak 也没有被声明为 __strong。

    因此,根据 ARC,即使其内容分配在 foo 内,释放它的责任也必须驻留在某个地方。

    在哪里?

    请注意,这不是双指针的属性本身。但是你的分配模式。

    考虑:

    int ** arrayOfarrayOfInts = {
        {1, 2, 3, 4}    
    ,   {5, 6, 7, 8}
    } ;
    
    - (void) incrementElement: (int **) elem {
        ++(**elem) ;
    }
    
    - (void) bumpFirstColByOne {
        for (int i = 0 ; i < 2 ; ++ i) {
           int * arrayOfInt = arrayOfarrayOfInts[i] ;
           int ** second = &arrayOfInt[0] ;
           [self incrementElement: second] ;
        }
    }
    

    这里不需要 __autoreleasing,因为没有进行分配...

    【讨论】:

    • 那个int **arrayOfArrayOfInts 的例子是不正确的。数组不是指针,这不会编译。不要点燃/灌输错误的信念。
    • 你说errPtr既不弱也不强也是不正确的。默认情况下,未声明的“强度”是强的。
    • 确实编译[诚然带有可怕警告],但它运行。因此,为什么我在它的开头加上:warning: untested。欢迎您编辑和修复它,假设您确实看到了所提出的观点,但 ...
    • @verec 那么你编译了吗? int **aray = { some array intializer } 由于类型不兼容而无法编译。
    • @verec 我没有忽略你的主要观点。否则答案很好,但我不喜欢传播常见(和可怕)的恶作剧。此外,我可以很好地区分警告和错误。这不会导致警告,但会导致错误。 Like this.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-29
    • 2019-06-07
    • 1970-01-01
    • 2013-01-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多