2016-03-23 9 views
0

Eine seltsame Sache, die mich stolpert. Fühlt sich an, als gäbe es ein einfaches "In Swift 2 machen wir immer (oder nie) dieses", dass ich vermisse, aber ich kann es nicht sehen.Swift, Singletons, Variablen, die Funktionen darstellen

ich eine Gehirn-Klasse, gemeint als Singleton verwendet werden:

class Brain: NSObject { 

    static var sharedInstance : Brain? 

    var languageLoadedAndReadyFunction = languageLoadedAndReadyImplementation 

    init() { 
     NSNotificationCenter.defaultCenter().addObserver(self, 
      selector: "languageLoadedAndReady", 
      name: Notifications.LanguageLoadedAndReady, 
      object: nil) 
    } 

    func languageLoadedAndReadyImplementation() { 

     print("Got language Ready notification") 
    } 

    func languageLoadedAndReady() { 

     self.languageLoadedAndReadyFunction(self)() 
    } 

    class func get() -> Brain! { 

     return sharedInstance 
    } 

    //... 

    class func reset() { 

     sharedInstance = Brain()   
    } 

    deinit() { 

     NSNotificationCenter.defaultCenter().removeObserver(self) 
    } 
} 

In meinem Unit-Test für Gehirn:

func testBrainRegisterForNotificationWhenWakingUp() { 

    let expectation = expectationWithDescription("Function should be called when the notification is received") 

    var brain = Brain.get() 

    brain.languageLoadedAndReadyFunction = {brain ->() ->() in 
     { 

      expectation.fulfill() 

      Brain.reset() // <-- this sets sharedInstance to a new Brain 
     } 
    } 

    brain.startUp() // <-- this causes the languageLoadedAndReady event to arrive at the brain 

    waitForExpectationsWithTimeout(5) { 
     error in 
     if let error = error { 
      print("Error: \(error.localizedDescription)") 
     } 
    } 
} 

The way I benötigt, um die Funktion als „Gehirn definieren ->() ->() "fühlt sich schwerfällig an, aber es scheint ein Leckerbissen zu sein. Es ließ mich sehen, wann die Benachrichtigung eintraf, also konnte ich per Test verifizieren, dass wir uns korrekt verhalten. Die Deinit() - Methode wird aufgerufen, nachdem das Gehirn zurückgesetzt wurde, und zeigt mir an, dass das alte Gehirn aus dem Gedächtnis entfernt wird.

Heute bin ich einige neue Tests für TranslatorTests.swift Schreiben:

func testTranslatorCanTranslate() { 

    let expectation = expectationWithDescription("Function should be called when the notification is received2") 

    var brain = Brain.get() 

    brain.languageLoadedAndReadyFunction = {brain ->() ->() in 
     { 

      expectation.fulfill() 

      Brain.reset() 

      print("first TranslatorTests") 
     } 
    } 

    brain.startUp() // <-- 'brain' is different here than in BrainTests.swift, causes 
        // the closure in BrainTests.swift to be called. 

    waitForExpectationsWithTimeout(5) { 
     error in 
     if let error = error { 
      print("Error: \(error.localizedDescription)") 
     } 
    } 

    translator.setActiveLanguage("en") 

    let resultString = translator.translate("about_title") 

    XCTAssertEqual(resultString, "About") 
} 

func testTranslatorCanTranslateSecondWord() { 

    let expectation = expectationWithDescription("Function should be called when the notification is received3") 

    let brain = Brain.get() 

    brain.languageLoadedAndReadyFunction = {brain ->() ->() in 
     { 

      expectation.fulfill() 

      Brain.reset() 

      print("second TranslatorTests") 
     } 
    } 

    brain.startUp() // <-- 'brain' is different here than in BrainTests.swift, causes 
        // the closure in BrainTests.swift to be called. 

    waitForExpectationsWithTimeout(5) { 
     error in 
     if let error = error { 
      print("Error: \(error.localizedDescription)") 
     } 
    } 

    translator.setActiveLanguage("en") 

    let resultString = translator.translate("actmon_ready") 

    XCTAssertEqual(resultString, "Ready to upload") 
} 

Wenn ich die Tests ausführen, bekomme ich die bizarrsten Fehler: der Verschluss in BrainTests.swift aufgerufen wird, wenn die TranslatorTests. swift wird ausgeführt. Dadurch wird die expectation.fullfill() -Methode ein zweites Mal aufgerufen, was zu einem Absturz führt.

Übrigens gibt es innerhalb der Schließung kein "Selbst", aber wenn ich eine Ebene in der Aufrufliste nach oben gehe, bezieht sich das "Selbst" auf die vorherige Instanz des Gehirns. Das führt mich zu der Annahme, dass das Gehirn ->() ->() -Syntax das Problem ist.

Dies schreckt mich. Das "Gehirn" hat vor jeder Schließung eine andere Adresse, was mir anzeigt, dass es sich um eine andere Instanz handelt. In der Tat, das alte Gehirn wurde von diesem Punkt entweiht, wie könnte es also heißen?

Ich hätte gedacht, dass die Zuweisung an die Variable für eine Instanz bedeuten würde, dass wir der Schließung eine neue Funktion geben, die für diese Instanz ausgeführt werden soll.

Kann mir das jemand erklären? Bitte benutze kleine Worte, ich fühle mich etwas weniger intelligent als beim Schreiben dieses Codes.

Antwort

0

In meiner Forschung, stolperte ich über diese beiden Artikel, die geholfen haben:

http://oleb.net/blog/2014/07/swift-instance-methods-curried-functions/ https://devforums.apple.com/message/1008188#1008188

, die mir erklärt, warum der Anruf „{Gehirn ->() ->() in .. . "

Das Problem, das ich in meinem Code hatte war natürlich, dass die Instanzvariable wie dies in meiner Testdatei erklärt wurde:

class BrainTests: XCTestCase { 

    var brain: Brain! 

    override func setUp() { 

     super.setUp() 
     brain = Brain.get() 
    } 

    override func tearDown() { 

     super.tearDown() 
     Brain.reset() 
    } 

    func testBrainExists() { 

     XCTAssertNotNil(brain) 
    } 

Also, wenn ich Brain.reset wurde() aufgerufen wird die fallen alter Singleton und erstelle einen neuen, da war noch ein Zeiger auf das Original.

Durch Entfernen der Instanzvariable wurde das Gehirn wie erwartet zurückgesetzt und die Callbacks funktionierten. Ich konnte Callbacks aus jedem Testfall setzen, um alle meine Tests zu bestehen.

Gut, das war eine hinterhältige Sache!