1

My function below creates a CFSocket and tries to create input and output streams with the original function's owner as the delegate.

@objc
public class BonjourPublisher: NSObject, NSNetServiceDelegate, NSStreamDelegate {
    private func hookUpSocket(fd: CFSocketNativeHandle) throws {
        var context = CFSocketContext()

        var selfPtr = self
        withUnsafeMutablePointer(&selfPtr) {
            context.info = UnsafeMutablePointer<Void>($0)
        }

        serviceSocket = withUnsafePointer(&context) {
            CFSocketCreateWithNative(nil, fd, CFSocketCallBackType.AcceptCallBack.rawValue, CallbackListen, UnsafePointer<CFSocketContext>($0))
        }

        guard serviceSocket != nil && CFSocketIsValid(serviceSocket) else {
            throw BonjourServerError.CreatingNativeSocket
        }

        serviceRunLoopSource = CFSocketCreateRunLoopSource(nil, serviceSocket, 0)

        guard serviceRunLoopSource != nil && CFRunLoopSourceIsValid(serviceRunLoopSource) else {
            throw BonjourServerError.CreatingSocketRunLoopSource
        }

        CFRunLoopAddSource(CFRunLoopGetCurrent(), serviceRunLoopSource, kCFRunLoopCommonModes)
    }
}

func CallbackListen(s: CFSocket!, callbackType: CFSocketCallBackType, address: CFData!, data: UnsafePointer<Void>, info: UnsafeMutablePointer<Void>) {
    let fd = UnsafePointer<CFSocketNativeHandle>(data)
    var readStream: Unmanaged<CFReadStreamRef>?
    var writeStream: Unmanaged<CFWriteStreamRef>?

    CFStreamCreatePairWithSocket(nil, fd.memory, &readStream, &writeStream)

    let inputStream: NSInputStream = readStream!.takeRetainedValue()
    let outputStream: NSOutputStream = writeStream!.takeRetainedValue()

    inputStream.setProperty(kCFBooleanTrue, forKey: kCFStreamPropertyShouldCloseNativeSocket as String)

    let publisherPtr = UnsafeMutablePointer<BonjourPublisher>(info)
    let publisher: BonjourPublisher = publisherPtr.memory
    inputStream.delegate = publisher
    outputStream.delegate = publisher

    inputStream.open()
    outputStream.open()
}

But the first line to refer to publisherPtr.memory gets an EXC_BAD_ACCESS exception. What's the problem here? Is it an ARC issue, or am I messing up my pointer passing?

Dov
  • 15,530
  • 13
  • 76
  • 177
  • 1
    This could be what you are looking for: [Swift 2 - UnsafeMutablePointer to object](http://stackoverflow.com/questions/30786883/swift-2-unsafemutablepointervoid-to-object). More info here: [How to cast self to UnsafeMutablePointer type in swift](http://stackoverflow.com/questions/33294620/how-to-cast-self-to-unsafemutablepointervoid-type-in-swift). – Martin R Nov 10 '15 at 21:36
  • 1
    The problem in your code is that you pass the address of the *local variable* `var selfPtr` to the callback. That address becomes invalid as soon as the `hookUpSocket()` function returns. – Martin R Nov 10 '15 at 21:40
  • @MartinR So I switched to opaque pointers, as the first link recommends, and that got rid of the exceptions. I've got some other issues, unfortunately, but it looks like that answered the question. Would you like to post an answer? – Dov Nov 10 '15 at 21:59
  • If that link answers your question then we can close this as a duplicate. – I am not sure but don't you need to register the input/output streams with the run loop as well? Also these are local variables in the callback, better make them properties of the BonjourPublisher class. – Martin R Nov 10 '15 at 22:02
  • @MartinR Bam, right on both counts. Thanks! I voted to close as a duplicate of the first link, which is the one that worked for me. – Dov Nov 10 '15 at 22:07

1 Answers1

1

(From the above comments:) There are several problems here:

  • You pass the address of the local variable var selfPtr to the callback. That address becomes invalid as soon as the hookUpSocket() function returns. See Swift 2 - UnsafeMutablePointer<Void> to object for a way to pass a pointer to self to the callback, more information here: How to cast self to UnsafeMutablePointer<Void> type in swift.

  • inputStream and outputStream must be registered with a run loop.

  • inputStream and outputStream are local variables of the callback function so they are deallocated when the function returns. They should be properties of your class instead.

Community
  • 1
  • 1
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382