如你說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
),然後在繪製模式之前解除分配 - 嘗試在繪圖回調中使用懸掛指針時,您會得到未定義的行爲。
隨着Unmanaged
:
您可以通過參考作爲保留+1不透明指針與passRetained(_:).toOpaque()
你可以拿回這個指針與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
堆分配箱其傳遞到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)
}
}
現在,這也需要的任何保留週期的擔憂護理。
感謝您的信息。這是我在Swift中處理「指針」的第一個。在搜索時我遇到了'unsafeBitCast'的使用。它適用於我的情況,但很高興知道有更好的方法。在我真正的代碼中,我實際上是將'CALayer'的實例作爲'info'參數傳遞。這是'createPattern'方法內創建的局部變量。它在我的代碼中使用'unsafeBitCast'工作得很好。包裝「CALayer」類型的局部變量的更安全的方法是什麼? – rmaddy
@rmaddy我假設你將'CALayer'實例局部變量添加到圖層層次結構中,然後保留它(或其他一些東西保留它)?這是我能想到爲什麼它會使用'unsafeBitCast'的唯一方法。除非存在保留週期問題(即'CALayer'實例保留對'CGPattern'的引用),否則我仍然建議在將'CALayer'實例保留爲'Unmanaged'(與第一個示例中的完全相同)之前,將它傳遞給'info:'參數 - 只是防止它在繪製模式之前被釋放。 – Hamish
如果存在保留週期問題,您可以隨時創建一個'Weak'類包裝器,該包裝器對給定圖層持有一個弱引用,然後傳遞保留的包裝器。然而,當然,如果給定的'CALayer'總是給定'CGPattern'的唯一擁有者,那麼你不應該做任何額外的保留(有效地使它成爲「無主」)。 – Hamish