2016-04-28 22 views
8

Considerare le funzioni Cocoa C private-ancora-di-documentate _NSLogCStringFunction() e _NSSetLogCStringFunction(). _NSLogCStringFunction() restituisce un puntatore alla funzione C utilizzata dal runtime Objective-C dietro le quinte per NSLog() e _NSSetLogCStringFunction() consente agli sviluppatori di specificare la propria funzione C per la registrazione. Ulteriori informazioni su entrambe queste funzioni sono disponibili in this Stack Overflow question e this WebObjects support article.Rappresentazione di funzioni NULL da puntatori a funzioni C in Swift

In C, posso passare in un puntatore a funzione NULL per _NSSetLogCStringFunction():

extern void _NSSetLogCStringFunction(void(*)(const char*, unsigned, BOOL)); 

_NSSetLogCStringFunction(NULL); // valid 

Comunque, sto correndo in alcuni problemi quando provo a fare questo in puro Swift:

/// Represents the C function signature used under-the-hood by NSLog 
typealias NSLogCStringFunc = (UnsafePointer<Int8>, UInt32, Bool) -> Void 

/// Sets the C function used by NSLog 
@_silgen_name("_NSSetLogCStringFunction") 
func _NSSetLogCStringFunction(_: NSLogCStringFunc) -> Void 

_NSSetLogCStringFunction(nil) // Error: nil is not compatible with expected argument type 'NSLogCStringFunc' (aka '(UnsafePointer<Int8>, UInt32, Bool) ->()') 

Se provo a ignorare questo avviso in fase di compilazione con unsafeBitCast, il mio programma si arresta immediatamente con EXC_BAD_INSTRUCTION (come previsto, poiché la firma è errata):

let nullPtr: UnsafePointer<Void> = nil 
let nullFuncPtr = unsafeBitCast(nullPtr, NSLogCStringFunc.self) 
_NSSetLogCStringFunction(nullFuncPtr) // crash 

Come si rappresenta un puntatore di funzione NULL su (void *) o (void(*)(const char *, unsigned, BOOL))/(UnsafePointer<Int8>, UInt32, Bool) -> Void in Swift?

+3

lol, downvote istante per qualsiasi motivo - non c'era nemmeno il tempo di leggere l'intera questione. – luk2302

+1

@ luk2302 Indovina che ho un fan :) Questo è un nuovo record per me, -1 in 44 secondi. – JAL

risposta

5

La mappatura Swift del (Objective-) dichiarazione C

extern void _NSSetLogCStringFunction(void(*)(const char*, unsigned, BOOL)); 

è

public func _NSSetLogCStringFunction(_: (@convention(c) (UnsafePointer<Int8>, UInt32, ObjCBool) -> Void)!) 

La soluzione più semplice sarebbe quella di mettere l'Objective-C extern dichiarazione in un header Objective-C file e includi quello dall'intestazione del bridging.

In alternativa, in puro Swift Va

typealias NSLogCStringFunc = @convention(c) (UnsafePointer<Int8>, UInt32, ObjCBool) -> Void 

@_silgen_name("_NSSetLogCStringFunction") 
func _NSSetLogCStringFunction(_: NSLogCStringFunc!) -> Void 

In entrambi i casi, il parametro di funzione è un modo implicito da scartare facoltativa, e si può chiamare con nil. Esempio:

func myLogger(message: UnsafePointer<Int8>, _ length: UInt32, _ withSysLogBanner: ObjCBool) -> Void { 
    print(String(format:"myLogger: %s", message)) 
} 

_NSSetLogCStringFunction(myLogger) // Set NSLog hook. 
NSLog("foo") 
_NSSetLogCStringFunction(nil) // Reset to default. 
NSLog("bar") 

uscita:

 
myLogger: foo 
2016-04-28 18:24:05.492 prog[29953:444704] bar 
+0

Ugh, davvero? Tutto quello che dovevo fare era cambiare 'Bool' in' ObjCBool'? -_- Ci sono troppi tipi Bool in Swift. Grazie Martin. – JAL

+0

@ JAL: E '@convention (c)' e il parametro implicitamente non scartato. –

+0

In realtà, non penso che sia richiesto ObjCBool'. Sembra che mi mancasse semplicemente '@convention (C)' e il parametro implicitamente senza scartare. Usare 'Bool' con gli altri due sembra funzionare. Stai vedendo la stessa cosa? – JAL