在 32 位 iOS 设备中,快速闭包中的 BOOL 签名是错误的

The signature of BOOL in a swift closure is wrong in a 32-bit iOS device

提问人:Yanni 提问时间:12/31/2020 最后编辑:Yanni 更新时间:1/3/2021 访问量:129

问:

BOOL 的签名通常在 objective-c, Reference 中是“B”。

但在 32 位设备上。BOOL 的签名是“c”。参考

有趣的发现

有趣的是,在 32 位设备上,快速闭包中的 BOOL 是“B”(应该是“c”)。

例如,这是一个快速关闭。

let closure = {_ in        
} as @convention(block) (Bool) -> Void

32 位设备上,闭包的签名是 v8@?0B4。按理说,它应该是 v8@?0c4

如何验证?

sh_blockSignature(C 代码)是从块中获取签名的函数

测试.m

enum {
    // Set to true on blocks that have captures (and thus are not true
    // global blocks) but are known not to escape for various other
    // reasons. For backward compatibility with old runtimes, whenever
    // BLOCK_IS_NOESCAPE is set, BLOCK_IS_GLOBAL is set too. Copying a
    // non-escaping block returns the original block and releasing such a
    // block is a no-op, which is exactly how global blocks are handled.
    BLOCK_IS_NOESCAPE      =  (1 << 23),
    
    BLOCK_HAS_COPY_DISPOSE =  (1 << 25),
    BLOCK_HAS_CTOR =          (1 << 26), // helpers have C++ code
    BLOCK_IS_GLOBAL =         (1 << 28),
    BLOCK_HAS_STRET =         (1 << 29), // IFF BLOCK_HAS_SIGNATURE
    BLOCK_HAS_SIGNATURE =     (1 << 30),
};

struct Block_literal_1 {
    void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
    int flags;
    int reserved;
    void (*invoke)(void *, ...);
    struct Block_descriptor_1 {
        unsigned long int reserved;         // NULL
        unsigned long int size;         // sizeof(struct Block_literal_1)
        // optional helper functions
        void (*copy_helper)(void *dst, void *src);     // IFF (1<<25)
        void (*dispose_helper)(void *src);             // IFF (1<<25)
        // required ABI.2010.3.16
        const char *signature;                         // IFF (1<<30)
    } *descriptor;
    // imported variables
};

const char * _Nullable sh_blockSignature(id block)
{
    struct Block_literal_1 *layout = (__bridge void *)block;
    if (!(layout->flags & BLOCK_HAS_SIGNATURE))
        return nil;
    
    void *descRef = layout->descriptor;
    descRef += 2 * sizeof(unsigned long int);
    
    if (layout->flags & BLOCK_HAS_COPY_DISPOSE)
        descRef += 2 * sizeof(void *);
    
    if (!descRef) return nil;
    
    const char *signature = (*(const char **)descRef);
    return signature;
}

测试2.swift

let closure = {_ in 
} as @convention(block) (Bool) -> Void
let p = sh_blockSignature(closure)!
let signature = String.init(cString: p)  
print(signature)  // "v8@?0B4"

这是 32 位设备上的快速错误吗?

Objective-C Swift 布尔 闭包 签名

评论

1赞 Carl Lindberg 1/3/2021
BOOL 在 32 位设备上为“c”是为了与现有二进制文件向后兼容(因为历史上 C 中没有 bool 类型,因此使用了 char)。如果有一个新平台,比如 WKInterface,他们可能会在 32 位平台上使用“B”。我不认为这种差异会导致任何兼容性问题——B 仍然是一个字节,就像 char 一样。这种差异是否表现为某个地方的问题?
0赞 Yanni 1/3/2021
感谢您的信息,@CarlLindberg。是的,我遇到了一个问题。我写了一个工具来吸引 swift github.com/623637646/SwiftHook .我需要验证快速闭包是否与该方法匹配。所以在 32 咬设备中。BOOL in 方法是,但 swift 闭包中的 BOOL 是 。它们不匹配,所以我的框架不能很好地工作。我通过这个提交解决了这个问题。github.com/623637646/SwiftHook/commit/......cB
0赞 Carl Lindberg 1/3/2021
是的 -- 几年前不得不在 OCMock 中处理这个问题(参见提交),稍后在此提交中进行了更全面的推广,以处理比较签名,其中一个具有不透明的结构指针,另一个具有详细信息 - 确实是相同的类型,但文本不同。其他人已经修复了错误并处理了一些C++邪恶,今天它甚至更加复杂。

答: 暂无答案