2014-10-22 4 views
8

ich meinen Code hatte in einem anderen Projekt arbeiten, in einer Klasse mit der folgenden Signatur:NSStreamDelegate nicht NSStreamEvent.HasSpaceAvailable Empfang:

class ViewController: UIViewController, NSStreamDelegate, UITextFieldDelegate { 

Dann zog ich die Verbindung zu seiner eigenen Klasse, so kann ich möglicherweise wiederzuverwenden in jeder Verbindung:

class XMPPConnection: NSObject, NSStreamDelegate 

Als ich das tat, zog ich alle viewDidLoad() Code in init(). Ich habe auch versucht, diesen init Code in eine separate Funktion zu setzen und diese Funktion nach der Instanziierung der Klasse aufzurufen. Das hat nichts geändert.

ich zwischen den zwei Projekten zu wechseln, den alten und neuen, nur um sicherzugehen, dass es nicht ein Serverproblem ist, und das zu tun, bestätigt, dass es nicht.

Nach jedem Lauf der Anwendung ist das Ergebnis anders aus. Es ruft entweder nicht die HasSpaceAvailable und sitzt nur dort, oder es ist ein (lldb) Fehler auf Thread 1 in meiner Klasse geworfen. Dieser Fehler kann zwar mit der Facebook-Integration zusammenhängen, aber mit lldb gibt es nicht viel zu sehen. Bei jedem Durchlauf wird HasSpaceAvailable jedoch nie aufgerufen, im Gegensatz zum anderen Projekt.

Hier ist der vollständige Code der NSStreamDelegate, so gibt es keine Verwirrung. Diese Klasse ist eine ziemlich standardmäßige Verbindungsmethode unter Verwendung des XMPP-Protokolls.

import UIKit 
import Foundation 


class XMPPConnection: NSObject, NSStreamDelegate { //NSObject 

    var input : NSInputStream? 
    var output: NSOutputStream? 

    //let XMLStream: String = "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' version='1.0' xmlns='jabber:client' to='mydomain.com' xml:lang='en' xmlns:xml='http://www.w3.org/XML/1998/namespace'>" 
    let XMLStream: String = "<stream:stream to='mydomain.com' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>" 
    var XMLAuth: String? 
    let XMLStreamEnd: String = "</stream:stream>" 
    let XMLResource: String = "<iq type='set' id='bind_1'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><resource>OneSide</resource></bind></iq>" 
    let XMLSession: String = "<iq type='set' id='sess_1'><session xmlns='urn:ietf:params:xml:ns:xmpp-session'/></iq>" 
    let XMLStartTLS: String = "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"; 
    var messagesToBeSent:[String] = [] 
    var lastSentMessageID = 0 
    var lastReceivedMessageID = 0 


    init(facebookID: String) { 
     super.init() 
     let username = "[email protected]" //should hash device ID 
     let password = "123456" //hash it 
     var UTF8AuthStr = "\0\(username)\0\(password)".dataUsingEncoding(NSUTF8StringEncoding) 
     let Base64Str = UTF8AuthStr!.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.fromRaw(0)!) 
     XMLAuth = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>\(Base64Str)</auth>" 
     //println(XMLAuth) 

     self.connectToSocket("mydomain.com", port: 5222) 
     send(self.XMLStream) 
     //send(self.XMLStartTLS) 
     /*send(self.XMLAuth!) 
     send(self.XMLStream) 
     send(self.XMLResource) 
     send(self.XMLSession)*/ 
     //sendMessage("hi") 


    } 

    func connectToSocket(host: String, port: Int) { 

     NSStream.getStreamsToHostWithName(host, port: port, inputStream: &(self.input), outputStream: &(self.output)) 

     self.input!.delegate = self 
     self.output!.delegate = self 

     self.input!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) 
     self.output!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) 

     self.input!.open() 
     self.output!.open() 

     println("Connected") 


     //let bytesWritten = self.output!.write(UnsafePointer(data.bytes), maxLength: data.length) 

     //println(bytesWritten) 


    } 

    //The delegate receives this message when a given event has occurred on a given stream. 
    func stream(theStream: NSStream!, handleEvent streamEvent: NSStreamEvent) { 

     println("Message received") 

     switch streamEvent { 
     case NSStreamEvent.None: 
      println("NSStreamEvent.None") 
     case NSStreamEvent.OpenCompleted: 
      println("NSStreamEvent.OpenCompleted") 
     case NSStreamEvent.HasBytesAvailable: 
      println("NSStreamEvent.HasBytesAvailable") 
      if let inputStream = theStream as? NSInputStream { 
       //println("is NSInputStream") 
       if inputStream.hasBytesAvailable { 
        //println("hasBytesAvailable") 
        let bufferSize = 1024 
        var buffer = Array<UInt8>(count: bufferSize, repeatedValue: 0) 

        var bytesRead: Int = inputStream.read(&buffer, maxLength: bufferSize) 
        //println(bytesRead) 
        if bytesRead >= 0 { 
         lastReceivedMessageID++ 
         var output: String = NSString(bytes: &buffer, length: bytesRead, encoding: NSUTF8StringEncoding) 
         //println("output is") 
         println(output) 
        } else { 
         println("error") 
         // Handle error 
        } 
       } 
      } 
     case NSStreamEvent.HasSpaceAvailable: 
      println("NSStreamEvent.HasSpaceAvailable") 
      send(nil) //send next item 
      //send next message or 
      //what if there is no next message to send, and instead waiting user input? 
     case NSStreamEvent.ErrorOccurred: 
      println("NSStreamEvent.ErrorOccurred") 
     case NSStreamEvent.EndEncountered: 
      println("NSStreamEvent.EndEncountered") 
     default: 
      println("default") 
     } 
    } 

    func send(message:String?){ 
     if (self.output!.hasSpaceAvailable){ //stream ready for input 
      //println("true hasSpaceAvailable") 
      var data:NSData 
      var thisMessage:String 
      if message == nil{ // no message specified 
       if messagesToBeSent.count != 0{ //messages waiting to be sent 
        thisMessage = messagesToBeSent[0] 
        data = messagesToBeSent[0].dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! 
        messagesToBeSent.removeAtIndex(0) 
       } 
       else{ //no data to be sent 
        //no message specified and nothing to be sent 
        return 
       } 
      } 
      else{ 
       thisMessage = message! 
       data = message!.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! 
      } 

      //println("Sent the following") 
      wait() 
      let bytesWritten = self.output!.write(UnsafePointer(data.bytes), maxLength: data.length) 
      lastSentMessageID++ 
      //println(thisMessage) 
      //println("Message sent to server and response is") 
      //println(bytesWritten) //int count 
     } 
     else{ //steam busy 
      println("no space available in stream") 
      if message != nil{ 
       messagesToBeSent.append(message!) 
      } 
     } 
    } 

    func sendMessage(message:String, from:String, to:String){ 
     let xmlMessage = "<message to='\(to)@mydomain.com' from='\(from)@mydomain.com' type='chat' xml:lang='en'> <body>\(message)</body></message>" 
     send(xmlMessage) 
    } 


    func wait() { 
     while true { 
      //println("waiting") 
      if lastSentMessageID == lastReceivedMessageID { 
       break 
      } 

      NSRunLoop.currentRunLoop().runUntilDate(NSDate(timeIntervalSinceNow: 0.1)); 
      NSThread.sleepForTimeInterval(0.1) 
     } 
    } 

} 

So kann ich 2 Dinge sehen, die das verursacht haben können. Entweder man verschiebt es in seine eigene Klasse und macht eine Instanz davon oder die Änderung der Vererbung. Das Nachdenken über die erste Möglichkeit, ich bin auf der Suche in die Threading-Codezeilen: self.input!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)

Nach dem streamStatus.toRaw() prüft, heißt es 1 die NSStreamStatusOpening ist. Ich bin mir nicht sicher, ob sich das jemals ändert.

Antwort

3

Ich könnte das Problem reproduzieren, wenn das XMPPConnection in einer lokalen Variablen, z.

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 

    let conn = XMPPConnection(facebookID: "...") 
    return true 
} 

Die Instanz wird freigegeben, wenn diese Methode zurückgibt. Auf der anderen Seite zeigen die Stream Delegierten immer noch auf die Instanz, dies verursacht die Abstürze. Die delegate Eigenschaft NSStream als

erklärt
unowned(unsafe) var delegate: NSStreamDelegate? 

die den Swift-Äquivalent von „zuweisen“, auch bekannt als „unsafe_unretained“ ist. Daher die Delegierten Einstellung behält nicht das Objekt, und Aufheben der Zuordnung das Objekt stellt nicht die Eigenschaft nil (wie für schwache Referenzen).

Wenn die Instanz in einer Eigenschaft gespeichert ist, z.

class AppDelegate: UIResponder, UIApplicationDelegate { 

    var window: UIWindow? 
    var conn : XMPPConnection! 

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 

     conn = XMPPConnection(facebookID: "...") 
     return true 
    } 

    // ... 
} 

dann funktionierte Ihr Code in meinem Test richtig.

-1
  CFStreamCreatePairWithSocketToHost(nil, serverAddress, Server, &readStream, &writeStream) 

CFStreamCreatePairWithSocketToHost(nil, website!.host, Server, &readStream, &writeStream) 

    CFReadStreamSetProperty(readStream!.takeUnretainedValue(), kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue) 
    CFWriteStreamSetProperty(writeStream!.takeUnretainedValue(), kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue) 
      inputStream = readStream!.takeUnretainedValue(); 
      outputStream = writeStream!.takeUnretainedValue(); 
Verwandte Themen