2016-04-28 9 views
8

Betrachten Sie die noch nicht dokumentierten Cocoa C-Funktionen _NSLogCStringFunction() und _NSSetLogCStringFunction(). _NSLogCStringFunction() gibt einen Funktionszeiger auf die C-Funktion zurück, die von der Objective-C-Laufzeit hinter den Kulissen für NSLog() verwendet wird, und _NSSetLogCStringFunction() ermöglicht Entwicklern, ihre eigene C-Funktion für die Protokollierung anzugeben. Weitere Informationen zu diesen beiden Funktionen finden Sie unter this Stack Overflow question und this WebObjects support article.Darstellen von NULL-Funktionszeigern zu C-Funktionen in Swift

In C, kann ich in einem NULL-Funktionszeiger auf _NSSetLogCStringFunction() passieren:

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

_NSSetLogCStringFunction(NULL); // valid 

aber ich laufe in einige Probleme, wenn ich versuche, dies in reiner Swift zu tun:

/// 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) ->()') 

Wenn ich versuche, diese Kompilierungswarnung mit unsafeBitCast zu umgehen, stürzt mein Programm einfach mit EXC_BAD_INSTRUCTION ab (wie erwartet, da die Signatur falsch ist):

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

Wie stelle ich einen NULL Funktionszeiger auf (void *) oder (void(*)(const char *, unsigned, BOOL))/(UnsafePointer<Int8>, UInt32, Bool) -> Void in Swift dar?

+3

lol, Instant downvote welchen Grund auch immer - war es nicht einmal Zeit, um die ganze Frage zu lesen. – luk2302

+1

@ luk2302 Schätze, ich habe einen Fan :) Das ist ein neuer Rekord für mich, -1 in 44 Sekunden. – JAL

Antwort

5

Die Swift-Mapping der (objektiv-) C Erklärung

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

ist

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

Die einfachste Lösung, um die Objective-C extern Erklärung in ein Objective-C-Header zu setzen wäre, Datei und enthalten, dass aus dem Bridging-Header.

Alternativ kann in reinen Swift es

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

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

In jedem Fall sein sollte, ist der Funktionsparameter ein implizit ungeöffneten optional, und Sie es mit nil aufrufen können. Beispiel:

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") 

Ausgang:

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

Ugh, wirklich? Alles, was ich tun musste, war "Bool" zu "ObjCBool" zu wechseln? -_- In Swift gibt es zu viele Bool-Typen. Danke Martin. – JAL

+0

@JAL: Und die 'convention (c)' und der implizit unverpackte Parameter. –

+0

Eigentlich glaube ich nicht, dass "ObjCBool" erforderlich ist. Sieht so aus, als hätte ich gerade den 'convention (C)' und den implizit nicht ausgepackten Parameter vermisst. Die Verwendung von 'Bool' mit den anderen beiden scheint zu funktionieren. Siehst du das Gleiche? – JAL