I'm investigating an odd problem that surfaced with macOS Sequoia related to CoreGraphics (CGColorSpaceCreateWithICCData()). I couldn't find a reliable way to track the lifetime (retain/releases) of a CGColorSpaceRef (any pointers appreciated) so I was hoping to use objc_setAssociatedObject() to attach an instance of my own class that would be deallocated when the "owning" CGColorSpaceRef is deallocated. In this way I could set a breakpoint on my own code, presumably invoked when the CGColorSpaceRef is itself going away.
While objc_setAssociatedObject() does seem to work, the results don't seem deterministic. Is it because objc_setAssociatedObject() simply won't work reliably with CF types?
Before I continue along this path, it helped to know if that machinery is just as reliable with bridgeable CFTypes as it is with regular Obj-C instances.
I would expect this to work, purely based on how CF and Obj-C interoperability works.
And a quick test suggests that it does in fact work. Consider the program at this end of this post. It prints:
point A
point B
chirrr… argh!
point C
indicating that uuidQ
is keeping canary
alive until it’s deallocated.
Remember that associated objects have many sharp edge cases, so it’s possible that you’re hitting one of these. For example, CG may be caching colour space objects, preventing their deallocation.
My first step in hunting problems like this is the Allocations instrument. If you leave “Discard events for freed memory” unchecked and then check “Record reference counts”, you can track the entire lifetime of an object starting with just its address.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
import Foundation
class Canary: NSObject {
deinit { print("chirrr… argh!") }
}
let key = malloc(0)!
var uuidQ: CFUUID? = nil
func main() {
print("point A")
autoreleasepool {
let canary = Canary()
let uuid = CFUUIDCreateFromString(nil, "D2F03482-81FD-460A-B1C8-5D7D0CDAF66F" as NSString)!
objc_setAssociatedObject(uuid, key, canary, .OBJC_ASSOCIATION_RETAIN)
uuidQ = uuid
}
print("point B")
autoreleasepool {
uuidQ = nil
}
print("point C")
}
main()