0

I have a myCGEventCallback function for CGEventTap that takes parameter "refcon:UnsafeMutableRawPointer?".

I pass my main ViewController as a pointer to the callback using

let pointer = UnsafeMutableRawPointer(Unmanaged.passRetained(self).toOpaque())

Then inside the callback, I access the ViewController using

let sender:ViewController = Unmanaged<T>.fromOpaque(refcon!).takeRetainedValue()

When the event occurs, the callback works fine. However, it only works 4 times. When the same event occurs for the fifth time, my app crashes, and the debug console just says "LLDB".

It looks like it crashes when I try to access the sender. "sender.someFunction()". It crashes before the function gets to run, so I assume it has a problem accessing the sender.

Is this because of poor memory management? Maybe I need to deallocate the pointer? If so, how and where would I do that?

Thanks!

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
jl303
  • 1,461
  • 15
  • 27
  • `takeRetainedValue()` performs a release of the object, therefore you shouldn't call it multiple times on an object that you've only retained once. What you probably want to do is use `takeUnretainedValue()` in your callback, and then use `.release()` when you know the callback can no longer be called – e.g see https://stackoverflow.com/a/44215903/2976878 – Hamish Oct 30 '18 at 13:26

1 Answers1

1

passRetained(self) increases the retain count for self (which is your view controller instance) by one. Each call to takeRetainedValue() decreases the retain count by one. Those calls must be properly balanced, otherwise the object might be destroyed too early.

In your case, where the pointer is created once, but used several times in a callback function, you should use the “unretained” conversion in the callback:

let sender = Unmanaged<ViewController>.fromOpaque(refcon!).takeUnretainedValue()

so that no ownership is transferred.

There are two patterns how to create the pointer:

  • If the callback is guaranteed to be active only during the lifetime of the view controller then you can create the pointer without retaining the instance:

    let pointer = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())
    
  • Otherwise, if the view controller instance should be kept alive while the callback is active, retain it while creating the pointer

    let pointer = UnsafeMutableRawPointer(Unmanaged.passRetained(self).toOpaque())
    

    and release it eventually, when the callback is no longer active:

    Unmanaged<ViewController>.fromOpaque(pointer).release()
    
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382