Objective-C 和 Swift 之間的細粒度互操作

當 API 標記為 NS_REFINED_FOR_SWIFT 時,在匯入到 Swift 時,它將以兩個下劃線(__)作為字首:

@interface MyClass : NSObject
- (NSInteger)indexOfObject:(id)obj NS_REFINED_FOR_SWIFT;
@end

生成的介面看起來是這樣的:

public class MyClass : NSObject {
    public func __indexOfObject(obj: AnyObject) -> Int
}

現在,你可以使用更 Swifty 副檔名替換 API 。在這種情況下,我們可以使用可選的返回值,過濾掉 NSNotFound

extension MyClass {
    // Rather than returning NSNotFound if the object doesn't exist,
    // this "refined" API returns nil.
    func indexOfObject(obj: AnyObject) -> Int? {
        let idx = __indexOfObject(obj)
        if idx == NSNotFound { return nil }
        return idx
    }
}

// Swift code, using "if let" as it should be:
let myobj = MyClass()
if let idx = myobj.indexOfObject(something) {
    // do something with idx
}

在大多數情況下,你可能希望限制 Objective-C 函式的引數是否為 nil。這是使用 _Nonnull 關鍵字完成的,該關鍵字限定任何指標或塊引用:

void
doStuff(const void *const _Nonnull data, void (^_Nonnull completion)())
{
    // complex asynchronous code
}

編寫完成後,只要我們嘗試將 nil 從 Swift 程式碼傳遞給該函式,編譯器就會發出錯誤:

doStuff(
    nil,  // error: nil is not compatible with expected argument type 'UnsafeRawPointer'
    nil)  // error: nil is not compatible with expected argument type '() -> Void'

_Nonnull 相反的是 _Nullable,這意味著在此論證中傳遞 nil 是可以接受的。_Nullable 也是預設值; 但是,明確指定它可以提供更多自我記錄和麵向未來的程式碼。

為了進一步幫助編譯器優化程式碼,你還可能需要指定塊是否轉義:

void
callNow(__attribute__((noescape)) void (^_Nonnull f)())
{
    // f is not stored anywhere
}

使用此屬性,我們保證不會儲存塊引用,也不會在函式執行完畢後呼叫塊。