正如你所说的in your answer,CGPatternDrawPatternCallback 定义为:
typealias CGPatternDrawPatternCallback =
@convention(c) (UnsafeMutableRawPointer?, CGContext) -> Void
@convention(c) 属性(仅出现在生成的标头中)意味着使用的函数值必须与 C 兼容,因此不能捕获任何上下文(因为 C 函数值只不过是指向一个函数,并且不存储额外的上下文对象)。
所以如果你想在函数中提供上下文,你需要将你自己的UnsafeMutableRawPointer? 传递给CGPattern's initialiser 的info: 参数。然后,这将在被调用时作为给定绘图模式函数的第一个参数传递。
为了将self传递给这个参数,你可以使用Unmanaged。这允许您在引用和不透明指针之间进行转换,并且与 unsafeBitCast 不同,还允许您在执行此操作时控制引用的内存管理。
鉴于我们不能保证createPattern() 的调用者会保留self,我们不能只是将它传递给info: 参数而不自己保留它。如果它在没有保留的情况下传递(例如使用unsafeBitCast),然后在绘制模式之前被释放 - 当您尝试在绘图回调中使用悬空指针时,您会得到未定义的行为。 p>
Unmanaged:
您可以使用 passRetained(_:).toOpaque() 将引用作为 +1 保留不透明指针传递
您可以使用 fromOpaque(_:).takeUnretainedValue() 从此指针取回引用(并且实例将保持保留)
然后您可以使用 fromOpaque(_:).release() 使用 +1 保留。当CGPattern 被释放时,你会想要这样做。
例如:
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)
}
}
或者,如果您想要 SomeShape 的值语义,一个更好的解决方案,您可以将其设为 struct。然后在创建模式时,您可以将其包装在 Context heap-allocated 框中,然后将其传递给 info: 参数:
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)
}
}
现在这也解决了任何保留周期问题。