2014-10-22 60 views
8

我有我的代码在其他项目的工作,在一类具有以下特征:NSStreamDelegate不接受NSStreamEvent.HasSpaceAvailable:

class ViewController: UIViewController, NSStreamDelegate, UITextFieldDelegate { 

然后我搬到了它自己的类连接,这样我就可以潜在地重用在每个连接:

class XMPPConnection: NSObject, NSStreamDelegate 

当我这样做,我感动了所有viewDidLoad()代码为init()。我也尝试将这个init代码放在一个单独的函数中,并在实例化该类之后调用该函数。这并没有改变任何事情。

我可以在2个项目中,新与旧之间切换,只是为了确保它不是一个服务器的问题,这样做,确认它不是。

每次运行应用程序后,结果都不一样。它或者不叫HasSpaceAvailable,只是坐在那里,或者在我的类class AppDelegate: UIResponder, UIApplicationDelegate, FBLoginViewDelegate的线程1上出现(lldb)错误。尽管这个错误可能与Facebook整合有关,但lldb没什么可看的。然而,与其他项目不同,每次运行都不会调用HasSpaceAvailable

这里的NSStreamDelegate的完整的代码,所以没有混乱。这个类意味着成为一个使用XMPP协议的相当标准的连接方法。

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

} 

所以我可以看到2件事情可能造成这种情况。要么把它移到它自己的类中,并且创建它的一个实例,或者改变继承。关于第一个方法可行思考,我期待到的代码穿线行:self.input!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)

检查streamStatus.toRaw()后,它说1这是NSStreamStatusOpening。我不确定这是否会改变。

回答

3

如果XMPPConnection存储在局部变量, 例如我可以重现该问题

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

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

当此方法返回时,实例将被释放。另一方面流代理仍然指向实例,这会导致崩溃。的NSStreamdelegate 属性被声明为

unowned(unsafe) var delegate: NSStreamDelegate? 

这是“分配”又名“unsafe_unretained”雨燕等同。因此 设置委托不保留该对象,并且释放对象 不会将该属性设置为nil(对于弱引用)。

如果该实例被存储在属性,例如

class AppDelegate: UIResponder, UIApplicationDelegate { 

    var window: UIWindow? 
    var conn : XMPPConnection! 

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

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

    // ... 
} 

然后你的代码在我的测试中正常工作。

-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();