2016-03-29 11 views
3

Wie bekomme ich Zugriff auf die Klasse "self" Instanz zum Aufruf von Klassen-Instanz-Methoden mit dem folgenden Code. Wenn ich self.callSomeClassIntance(), wie gezeigt, versuche, bekomme ich einen "A C-Funktionszeiger kann nicht von einem Abschluss gebildet werden, der Kontext" Fehler vom Compiler erfasst. Ich versuche info.callSomeClassInstance(), aber dies wird einen "no member callSomeClassInstance" Fehler geben. Code wird Zeit korrekt ausgelöst, wenn die eine Codezeile xxxx.callSomeClassIntance() entfernt wird.Swift CFRunLoopTimerCreate - Wie bekomme ich "Selbst" in Timer-Rückruf

import Foundation 

class Foo { 
    func callSomeClassIntance() {} 

    func start() { 
     let runLoop : CFRunLoopRef = CFRunLoopGetCurrent(); 
     var context = CFRunLoopTimerContext(version: 0, info: unsafeBitCast(self, UnsafeMutablePointer<Void>.self), retain: nil, release: nil, copyDescription: nil) 

     let timer : CFRunLoopTimerRef = CFRunLoopTimerCreate(kCFAllocatorDefault, 0.1, 3.0, 0, 0, cfRunloopTimerCallback(), &context); 

     CFRunLoopAddTimer(runLoop, timer, kCFRunLoopCommonModes); 

     CFRunLoopRun() 
    } 

    func cfRunloopTimerCallback() -> CFRunLoopTimerCallBack { 

     return { (cfRunloopTimer, info) -> Void in 
      print("Fire timer...") 
      // need self context here to call class instance methods 
      self.callSomeClassIntance() 
     } 

    } 
} 
+0

Siehe auch http://stackoverflow.com/questions/30786883/swift-2-unsafutablepointervoid-to-object. –

+0

Ich denke, das ist ein anderes Problem. Er möchte in der Lage sein, das Selbst in die Callback-Funktion zu überführen. Was kann getan werden, indem man Foo passiert! an die Funktion cfRunloopTimerCallback und mithilfe des übergebenen Parameters callSomeClassInstance aufrufen. –

+0

func cfRunloopTimerCallback (selfPtr: Foo) -> CFRunLoopTimerCallBack { return {(cfRunloopTimer, info) -> Void in print ("Fire Timer ...") // müssen hier selbst Kontextklasse Instanzmethoden nennen SelbstPtr.callSomeClassIntance() } } } –

Antwort

2

Wir brauchen nicht self zu erfassen, weil wir es schon vorbei sind in.

Wenn Sie den Kontext für Ihre Timer erstellen, sind Sie self in ein Format setzen, die den C-Code ermöglicht

unsafeBitCast(self, UnsafeMutablePointer<Void>.self) 

Dieser Code gibt einen void-Zeiger auf self: mit ihm, einen void-Zeiger zu behandeln. Und das ist, was Sie für das info Argument übergeben, wenn Sie Ihren Kontext erstellen.

Was auch immer Sie für das Argument info übergeben, wenn Sie Ihren Kontext erstellen, ist, was verwendet wird, um für das info Argument der CFRunLoopTimerCallback-Funktion übergeben. Also müssen wir die inverse Operation (unsafeBitCast(info, Foo.self)) zu diesem info Argumente gelten:

func cfRunloopTimerCallback() -> CFRunLoopTimerCallBack { 
    return { _, info in 
     let grabSelf = unsafeBitCast(info, Foo.self) 
     grabSelf.callSomeClassIntance() 
    } 
} 
3

Wie in How to use instance method as callback for function which takes only func or literal closure, die CFRunLoopTimerCallBack muss eine globale Funktion oder ein Verschluss sein, die nicht Kontext nicht erfassen. Insbesondere kann diese Schließung nicht erfassen self und muss daher konvertieren den Zeiger von info im Kontext zurück auf eine Instanz Zeiger .

Sie müssen nicht unbedingt die cfRunloopTimerCallback() Funktion kann ein Verschluss direkt als Argument übergeben werden:

class Foo { 
    func callSomeClassIntance() {} 

    func start() { 
     let runLoop = CFRunLoopGetCurrent(); 
     var context = CFRunLoopTimerContext() 
     context.info = UnsafeMutablePointer<Void>(Unmanaged.passUnretained(self).toOpaque()) 

     let timer = CFRunLoopTimerCreate(kCFAllocatorDefault, 0.1, 3.0, 0, 0, { 
      (cfRunloopTimer, info) -> Void in 

      let mySelf = Unmanaged<Foo>.fromOpaque(COpaquePointer(info)).takeUnretainedValue() 
      mySelf.callSomeClassIntance() 
     }, &context); 

     CFRunLoopAddTimer(runLoop, timer, kCFRunLoopCommonModes); 
     CFRunLoopRun() 
    } 
} 

Hier habe ich Unmanaged für die Konvertierungen zwischen Instanz-Zeiger und void-Zeiger verwendet. Es sieht komplizierter aus, betont aber, dass unverbindliche Referenzen herumgereicht werden. unsafeBitCast() wie in @ nhgrifs Antwort kann alternativ verwendet werden.

Sie können auch ähnliche Funktionen wie Objective-C __bridge, vergleichen How to cast self to UnsafeMutablePointer<Void> type in swift.