这是我的第一次尝试:
class SomeShape {
func createPattern() -> CGPattern? {
let bounds = CGRect(x: 0, y: 0, width: someWidth, height: someHeight)
let matrix = CGAffineTransform.identity
var callbacks = CGPatternCallbacks(version: 0, drawPattern: nil, releaseInfo: nil)
let res = CGPattern(info: nil, bounds: bounds, matrix: matrix, xStep: bounds.width, yStep: bounds.height, tiling: .noDistortion, isColored: true, callbacks: &callbacks)
return res
}
}
显然,这需要一个适当的值来为CGPatternCallbacks的drawPattern参数,我需要将self作为info参数传递给CGPattern初始化程序.
完成此操作的正确语法是什么?
如你所说 in your answer,CGPatternDrawPatternCallback被定义为:typealias CGPatternDrawPatternCallback =
@convention(c) (UnsafeMutableRawPointer?, CGContext) -> Void
@convention(c)属性(仅显示在生成的标题中)表示所使用的函数值必须与C兼容,因此无法捕获任何上下文(因为C函数值只不过是函数的原始指针,并且不要不存储额外的上下文对象).
因此,如果您想在函数中使用上下文,则需要传递自己的UnsafeMutableRawPointer?到参数CGPattern‘s initialiser.这将被调用时作为给定绘图模式函数的第一个参数传递.
为了将self传递给此参数,可以使用Unmanaged.这允许您在引用和不透明指针之间进行转换,并且与unsafeBitCast不同,还允许您在执行此操作时控制引用的内存管理.
鉴于我们无法保证createPattern()的调用者将保持自我保留,我们不能将其传递给info:参数而不保留它自己.如果它是在没有保留的情况下传递的(例如使用unsafeBitCast),然后在绘制模式之前被释放 – 在尝试在绘图回调中使用悬空指针时会出现未定义的行为.
使用非托管:
>您可以使用passRetained(_ :)传递引用作为1个保留的不透明指针.toOpaque()
>您可以使用fromOpaque(_ :)获取此指针的引用.takeUnretainedValue()(实例将保留)
>然后,您可以使用fromOpaque(_ :).release()来使用1 retain.在释放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的值语义,你可以使它成为一个结构更好的解决方案.然后在创建模式时,您可以将它包装在Context堆分配的框中,然后再将其传递给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)
}
}
现在这也考虑了任何保留周期问题.
