28

If I have a method like:

func someMethod(contextPtr: UnsafeMutablePointer<Void>)

how do I get the object from the contextPtr?

func someMethod(contextPtr: UnsafeMutablePointer<Void>){    
    let object:MyObject = contextPtr.memory
}

gives:

'Void' is not convertible to 'MyObject'

What's the secret sauce


More detail:

What I'm actually doing here is setting up a global callback function for SCNetworkReachability:

func callback(reachability:SCNetworkReachability, flags: SCNetworkReachabilityFlags, info: UnsafeMutablePointer<Void>) {

    let r:Reachability = info.memory
}

and then adding the callback as follows:

var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
var s = self
withUnsafeMutablePointer(&s) {
    context.info = UnsafeMutablePointer($0)
}
SCNetworkReachabilitySetCallback(reachability, callback, &context)
Ashley Mills
  • 50,474
  • 16
  • 129
  • 160

2 Answers2

45

This should work: pass the object pointer as an opaque unmanaged pointer to the callback:

context.info = UnsafeMutablePointer(Unmanaged.passUnretained(myObject).toOpaque())
SCNetworkReachabilitySetCallback(reachability, callback, &context) 

and retrieve in the callback via:

func callback(reachability:SCNetworkReachability, flags: SCNetworkReachabilityFlags, info: UnsafeMutablePointer<Void>) {

    let myObject = Unmanaged<MyObject>.fromOpaque(COpaquePointer(info)).takeUnretainedValue()

}

Of course this assumes that some strong reference to the object exists as long as the callback is installed, so that the object is not deallocated.

Update: Note that both conversions from object pointer to void pointer and back can be simplified if you are willing to use "unsafe" functions:

context.info = unsafeAddressOf(myObject)
// ...
myObject = unsafeBitCast(info, MyObject.self)

The generated assembly code is – as far as I can see – identical.

Update 2: See also How to cast self to UnsafeMutablePointer<Void> type in swift for more information about the "bridging" and some helper functions which can be used here.


Swift 3 update (Xcode 8 beta 6):

var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
context.info = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())

// ...

func callback(reachability:SCNetworkReachability, flags: SCNetworkReachabilityFlags, info: UnsafeMutableRawPointer?) {
    if let info = info {
        let myObject = Unmanaged<MyObject>.fromOpaque(info).takeUnretainedValue()
        // ...
    }
}
Community
  • 1
  • 1
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • @AshleyMills: Nice to see that you could use it in your Reachability project! – Martin R Sep 21 '15 at 15:38
  • That was the final key to the puzzle! – Ashley Mills Sep 21 '15 at 20:22
  • just noticed your update. Nice idea, but unfortunately this doesn't work for the problem I was trying to solve as `unsafeAddressOf` requires `AnyObject` and `SCNetworkReachabilityContext` is a `struct` – Ashley Mills Nov 11 '15 at 12:27
  • @AshleyMills: But the first solution doesn't work for struct either! And in your current Reachability project, at `context.info = UnsafeMutablePointer(Unmanaged.passUnretained(self).toOpaque())`, `self` is an instance of `class Reachability`, not a SCNetworkReachabilityContext. – Martin R Nov 11 '15 at 12:37
  • @AshleyMills: Here http://stackoverflow.com/questions/33294620/how-to-cast-self-to-unsafemutablepointervoid-type-in-swift some helper functions are defined to make the bridging easier. – Martin R Nov 11 '15 at 12:41
  • Brain fart… Please ignore my previous message! – Ashley Mills Nov 11 '15 at 12:42
  • 3
    Nice solution. I'm surprised how long it took me to find this, though. Given the reliance of certain low-level API's on unsafe pointers you'd think that a lot of people would need to know this! – Ash Dec 06 '15 at 10:30
  • the unsafeAddressOf and unsafeBitCast are the most useful yet. Due to a world of C that doesn't articulate these names as such, it's pretty much still hard to mentally shift to where we have to go to interact with C from Swift. :( – uchuugaka Feb 26 '16 at 21:51
  • Thanks for the Swift 3 update, but I still have a problem with trying to run your code in Swift 3. The line where you set `context.info`, gives the error `Value of type 'Unmanaged' has no member 'toOpaque'`. I have been trying to fix this for a while and found nothing. Any idea what to do? – yesthisisjoe Jun 16 '16 at 23:40
  • 1
    @yesthisisjoe: It seems that I updated the code using a Swift 3 preview release from swift.org. It does now compile with Xcode 8, can you check it again? Thanks for your feedback! – Martin R Jun 17 '16 at 00:15
  • @yesthisisjoe: And the syntax changed again with Xcode 8 beta 2 (to what I originally had written :) – Martin R Jul 15 '16 at 05:22
  • I will try this in the morning. Thanks for all the updates! – yesthisisjoe Jul 15 '16 at 05:24
  • In the Swift 3 version, why use `passRetained` with `takeUnretainedValue`? Should be `passUnretained`, right? – Ethan Sep 17 '16 at 18:44
  • Oh THANK YOU. I've been searching for an answer for this for days, and have performed so much ritual sacrifice that the neighbours are worrying about me. And damn you Apple, for such a terrible API. If it wasn't for the contributors of SO, I'd have given up on iOS dev a long time ago. – Womble Dec 13 '16 at 02:21
-2
struct S {
    var i: Int = 10
}
var first = S()

func foo(contextPtr: UnsafeMutablePointer<Void>){
    let pS = UnsafeMutablePointer<S>(contextPtr)
    pS.memory.i = 100
}
print(first.i) // 10
foo(&first)
print(first.i) // 100

if we need pass as UnsafeMutablePointer self to async function

import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true

import Foundation
// can be struct, class ...
class C {
    let queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT)
    var s: String = ""
    func foo() {
        var c = self
        dispatch_async(queue) { () -> Void in
            f(&c)
        }

    }
}

func f(pV: UnsafeMutablePointer<Void>) {
    let pC = UnsafeMutablePointer<C>(pV)
    sleep(1)
    print(pC.memory.s)
}

var c1: C? = C()
c1!.s = "C1"
c1!.foo()        // C1
var c2: C? = C()
c2!.s = "C2"
c2!.foo()        // C2
c1 = nil
c2 = nil
print("test")
user3441734
  • 16,722
  • 2
  • 40
  • 59
  • 2
    Your sample code is different than the question, though, because `C.foo()` calls `f()` synchronously, so `c` stays allocated. In the original question, `f()` is an asynchronous callback function, and so `c` would be released before `f()` is called. – Dov Nov 11 '15 at 15:42