Trouble using callbacks with CGPattern in Swift3
As you say in your answer, CGPatternDrawPatternCallback
is defined as:
typealias CGPatternDrawPatternCallback =
@convention(c) (UnsafeMutableRawPointer?, CGContext) -> Void
The @convention(c)
attribute (which only appears to show up in the generated header) means that the function value used must be compatible with C, and therefore cannot capture any context (because C function values are nothing more than raw pointers to a function, and don't store an additional context object).
So if you want to have context available in the function, you need to pass your own UnsafeMutableRawPointer?
to the info:
parameter of CGPattern
's initialiser. This will then be passed as the first parameter of the given draw pattern function upon being called.
In order to pass self
to this parameter, you can use Unmanaged
. This allows you to convert between references and opaque pointers and, unlike unsafeBitCast
, also lets you control the memory management of the reference when doing so.
Given that we have no guarantee that the caller of createPattern()
will keep self
retained, we cannot just pass it off to the info:
parameter without retaining it ourselves. If it were passed without retaining (for example with unsafeBitCast
), and was then deallocated before drawing the pattern – you would get undefined behaviour when attempting to use a dangling pointer in the drawing callback.
With Unmanaged
:
You can pass a reference as a +1 retained opaque pointer with
passRetained(_:).toOpaque()
You can get back a reference from this pointer with
fromOpaque(_:).takeUnretainedValue()
(and the instance will stay retained)You can then consume the +1 retain with
fromOpaque(_:).release()
. You'll want to do this when theCGPattern
is freed.
For example:
class SomeShape {
// the bounds of the shape to draw
let bounds = CGRect(x: 0, y: 0, width: 40, height: 40)
func createPattern() -> CGPattern? {
var callbacks = CGPatternCallbacks(version: 0, drawPattern: { info, ctx in
// cast the opaque pointer back to a SomeShape reference.
let shape = Unmanaged<SomeShape>.fromOpaque(info!).takeUnretainedValue()
// The code to draw a single tile of the pattern into "ctx"...
// (in this case, two vertical strips)
ctx.saveGState()
ctx.setFillColor(UIColor.red.cgColor)
ctx.fill(CGRect(x: 0, y: 0,
width: shape.bounds.width / 2, height: shape.bounds.height))
ctx.setFillColor(UIColor.blue.cgColor)
ctx.fill(CGRect(x: 20, y: 0,
width: shape.bounds.width / 2, height: shape.bounds.height))
ctx.restoreGState()
}, releaseInfo: { info in
// when the CGPattern is freed, release the info reference,
// consuming the +1 retain when we originally passed it to the CGPattern.
Unmanaged<SomeShape>.fromOpaque(info!).release()
})
// retain self before passing it off to the info: parameter as an opaque pointer.
let unsafeSelf = Unmanaged.passRetained(self).toOpaque()
return CGPattern(info: unsafeSelf, bounds: bounds, matrix: .identity,
xStep: bounds.width, yStep: bounds.height,
tiling: .noDistortion, isColored: true, callbacks: &callbacks)
}
}
Alternatively, a better solution if you want value semantics for SomeShape
, you could make it a struct
. Then when creating the pattern, you can just wrap it up in a Context
heap-allocated box before passing it off to the info:
parameter:
struct SomeShape {
// the bounds of the shape to draw
let bounds = CGRect(x: 0, y: 0, width: 40, height: 40)
func createPattern() -> CGPattern? {
final class Context {
let shape: SomeShape
init(_ shape: SomeShape) { self.shape = shape }
}
var callbacks = CGPatternCallbacks(version: 0, drawPattern: { info, ctx in
// cast the opaque pointer back to a Context reference,
// and get the wrapped shape instance.
let shape = Unmanaged<Context>.fromOpaque(info!).takeUnretainedValue().shape
// ...
}, releaseInfo: { info in
// when the CGPattern is freed, release the info reference,
// consuming the +1 retain when we originally passed it to the CGPattern.
Unmanaged<Context>.fromOpaque(info!).release()
})
// wrap self in our Context box before passing it off to the info: parameter as a
// +1 retained opaque pointer.
let unsafeSelf = Unmanaged.passRetained(Context(self)).toOpaque()
return CGPattern(info: unsafeSelf, bounds: bounds, matrix: .identity,
xStep: bounds.width, yStep: bounds.height,
tiling: .noDistortion, isColored: true, callbacks: &callbacks)
}
}
This now also takes care of any retain cycle concerns.
How to define a CGPatternDrawPatternCallback in swift
You can't do this in Swift 1.2 or earlier; you'd have to write this part of your code in Objective-C. To write a C function in Swift, you'll need to update to Swift 2.0 (available in the Xcode 7 beta currently).
Crash When Accessing refcon:UnsafeMutableRawPointer? Inside CGEventTap Callback
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 pointerlet pointer = UnsafeMutableRawPointer(Unmanaged.passRetained(self).toOpaque())
and release it eventually, when the callback is no longer active:
Unmanaged<ViewController>.fromOpaque(pointer).release()
How to cast self to UnsafeMutablePointer Void type in swift
An object pointer (i.e. an instance of a reference type) can be
converted to a UnsafePointer<Void>
(the Swift mapping of const void *
, UnsafeRawPointer
in Swift 3) and back. In Objective-C you would write
void *voidPtr = (__bridge void*)self;
//
MyType *mySelf = (__bridge MyType *)voidPtr;
(See 3.2.4 Bridged casts in the Clang ARC documentation for the precise meaning of these
casts.)
Swift has an Unmanaged
type for that purpose.
It is a bit cumbersome to use because it works with COpaquePointer
instead of UnsafePointer<Void>
. Here are two helper methods
(named after the Objective-C __bridge
cast):
func bridge<T : AnyObject>(obj : T) -> UnsafePointer<Void> {
return UnsafePointer(Unmanaged.passUnretained(obj).toOpaque())
// return unsafeAddressOf(obj) // ***
}
func bridge<T : AnyObject>(ptr : UnsafePointer<Void>) -> T {
return Unmanaged<T>.fromOpaque(COpaquePointer(ptr)).takeUnretainedValue()
// return unsafeBitCast(ptr, T.self) // ***
}
The "complicated" expression is only necessary to satisfy Swifts
strict type system. In the compiled code this is just a cast
between pointers. (It can be written shorter as indicated in the ***
comments
if you are willing to use "unsafe" methods, but the compiled
code is identical.)
Using this helper methods you can pass self
to a C function as
let voidPtr = bridge(self)
(or UnsafeMutablePointer<Void>(bridge(self))
if the C function requires
a mutable pointer), and convert it back to an object pointer – e.g.
in a callback function – as
let mySelf : MyType = bridge(voidPtr)
No transfer of ownership takes place, so you must ensure that self
exists as long as the void pointer is used.
And for the sake of completeness, the Swift equivalent of __bridge_retained
and __bridge_transfer
from Objective-C would be
func bridgeRetained<T : AnyObject>(obj : T) -> UnsafePointer<Void> {
return UnsafePointer(Unmanaged.passRetained(obj).toOpaque())
}
func bridgeTransfer<T : AnyObject>(ptr : UnsafePointer<Void>) -> T {
return Unmanaged<T>.fromOpaque(COpaquePointer(ptr)).takeRetainedValue()
}
bridgeRetained()
casts the object pointer to a void pointer and
retains the object. bridgeTransfer()
converts the
void pointer back to an object pointer and consumes the retain.
An advantage is that the object cannot be deallocated between the
calls because a strong reference is held. The disadvantage is that
the calls must be properly balanced, and that it can easily cause retain
cycles.
Update for Swift 3 (Xcode 8):
func bridge<T : AnyObject>(obj : T) -> UnsafeRawPointer {
return UnsafeRawPointer(Unmanaged.passUnretained(obj).toOpaque())
}
func bridge<T : AnyObject>(ptr : UnsafeRawPointer) -> T {
return Unmanaged<T>.fromOpaque(ptr).takeUnretainedValue()
}
func bridgeRetained<T : AnyObject>(obj : T) -> UnsafeRawPointer {
return UnsafeRawPointer(Unmanaged.passRetained(obj).toOpaque())
}
func bridgeTransfer<T : AnyObject>(ptr : UnsafeRawPointer) -> T {
return Unmanaged<T>.fromOpaque(ptr).takeRetainedValue()
}
The relevant changes to "unsafe pointers" are described in
- SE-0017 Change Unmanaged to use UnsafePointer
- SE-0107 UnsafeRawPointer API
Related Topics
How to Unittest Combine Cancellables
Swift5 Macos Imageresize Memory Issue
How to Access Nswindow from @Main App Using Only Swiftui
Remove Element from Collection During Iteration with Foreach
How to Use Contains Within a Swift Array Extension
@Noescape Attribute in Swift 1.2
How to Create Swift Class for Category
How to Make Class Methods/Properties in Swift
Why Use Required Initializers in Swift Classes
Find Difference in Seconds Between Nsdates as Integer Using Swift
May I Create an Instance of a Structure Using a Simple Int
Using Variables Outside of Completion Block
Formatting Decimal Places with Unknown Number
Compiler Error When Comparing Values of Enum Type with Associated Values
Conditional Property in Swiftui
How to Reason When I Have to Choose Between a Class, Struct and Enum in Swift