是否有 Swift 预处理器方法来检测桥接 Header 包含?

Is There A Swift preprocessor Way to Detect Bridging Header Includes?

我找到的唯一答案是 this,我对此并不满意。

我正在添加标准 MD5 转换器作为字符串扩展:

/* ###################################################################################################################################### */
/**
 From here: 
 I am not making this public, because it requires the common crypto in the bridging header.
 */
fileprivate extension String {
    /* ################################################################## */
    /**
     - returns: the String, as an MD5 hash.
     */
    var md5: String {
        let str = self.cString(using: String.Encoding.utf8)
        let strLen = CUnsignedInt(self.lengthOfBytes(using: String.Encoding.utf8))
        let digestLen = Int(CC_MD5_DIGEST_LENGTH)
        let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen)
        CC_MD5(str!, strLen, result)

        let hash = NSMutableString()

        for i in 0..<digestLen {
            hash.appendFormat("%02x", result[i])
        }

        result.deallocate()
        return hash as String
    }
}

它要求我将以下内容添加到我的桥接 header:

#import <CommonCrypto/CommonCrypto.h>

因为我想将它添加到一套可重复使用的工具中,所以我想看看是否有一种方法可以在编译时检测是否正在使用通用加密库。

有没有办法让我将其设置为条件编译?

没有也没什么大不了的;只是意味着我需要将其设置为单独的源文件。

值得注意的是,如果您使用 dlsym 访问 可以 调用 CC_MD5 而无需桥接 header。

import Foundation

typealias CC_MD5_Type = @convention(c) (UnsafeRawPointer, UInt32, UnsafeMutableRawPointer) -> UnsafeMutableRawPointer

let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: -2)
let CC_MD5 = unsafeBitCast(dlsym(RTLD_DEFAULT, "CC_MD5")!, to: CC_MD5_Type.self)

var md5 = Data(count: 16)
md5.withUnsafeMutableBytes {
    _ = CC_MD5("abc", 3, [=10=])
}

assert(md5 == Data(bytes: [0x90, 0x01, 0x50, 0x98, 0x3C, 0xD2, 0x4F, 0xB0, 0xD6, 0x96, 0x3F, 0x7D, 0x28, 0xE1, 0x7F, 0x72]))

这是我找到的解决方案。这是我原始变体的 mash-up,也是 Rob 的出色回答。它很有魅力(我用它来建立对 RFC2617 Digest authentication 的回应)。由于使用了 built-in 挂钩,我不再需要桥接 header,并且可以将其添加到我的字符串扩展集。

非常经典的正确答案案例,来自与我正在寻找的地方完全不同的地方。当发生这种情况时,我喜欢它。

给你:

public extension String {
    /* ################################################################## */
    /**
     From here: https://whosebug.com/q/24123518/879365, but modified from here: https://whosebug.com/a/55639723/879365
     - returns: an MD5 hash of the String
     */
    var md5: String {
        var hash = ""
        
        // Start by getting a C-style string of our string as UTF-8.
        if let str = self.cString(using: .utf8) {
            // This is a cast for the MD5 function. The convention attribute just says that it's a "raw" C function.
            typealias CC_MD5_Type = @convention(c) (UnsafeRawPointer, UInt32, UnsafeMutableRawPointer) -> UnsafeMutableRawPointer
            
            // This is a flag, telling the name lookup to happen in the global scope. No dlopen required.
            let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: -2)
            
            // This loads a function pointer with the CommonCrypto MD5 function.
            let CC_MD5 = unsafeBitCast(dlsym(RTLD_DEFAULT, "CC_MD5")!, to: CC_MD5_Type.self)
            
            // This is the length of the hash
            let CC_MD5_DIGEST_LENGTH = 16
            
            // This is where our MD5 hash goes. It's a simple 16-byte buffer.
            let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: CC_MD5_DIGEST_LENGTH)
            
            // Execute the MD5 hash. Save the result in our buffer.
            _ = CC_MD5(str, CUnsignedInt(str.count), result)
            
            // Turn it into a normal Swift String of hex digits.
            for i in 0..<CC_MD5_DIGEST_LENGTH {
                hash.append(String(format: "%02x", result[i]))
            }
            
            // Don't need this anymore.
            result.deallocate()
        }
        
        return hash
    }
}