Swift Language Interoperabilità a grana fine tra Objective-C e Swift


Esempio

Quando un'API è contrassegnata con NS_REFINED_FOR_SWIFT , verrà anteposta a due caratteri di sottolineatura ( __ ) se importati in Swift:

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

L' interfaccia generata si presenta così:

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

Ora puoi sostituire l'API con un'estensione più "Swifty". In questo caso, possiamo utilizzare un valore di ritorno opzionale , filtrando 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
}

Nella maggior parte dei casi potresti voler limitare o meno un argomento a una funzione Objective-C che potrebbe essere nil . Questo viene fatto usando la parola chiave _Nonnull , che qualifica qualsiasi riferimento a puntatore o blocco:

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

Con quello scritto, il compilatore deve emettere un errore ogni volta che proviamo a passare nil a quella funzione dal nostro codice Swift:

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

L'opposto di _Nonnull è _Nullable , il che significa che è accettabile passare nil in questo argomento. _Nullable è anche il valore predefinito; tuttavia, specificarlo consente esplicitamente un codice più auto-documentato e a prova di futuro.

Per aiutare ulteriormente il compilatore a ottimizzare il tuo codice, potresti anche voler specificare se il blocco sta eseguendo l'escape:

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

Con questo attributo promettiamo di non salvare il riferimento del blocco e di non chiamare il blocco dopo che la funzione ha terminato l'esecuzione.