Yep. Let's try C code first:
void testC(void) {
CGEventRef event = CGEventCreateKeyboardEvent(NULL, 1, 1);
printf("%d\n", myRetainCount(event)); // 1
printf("%d\n", CFGetRetainCount(event)); // 1
CGEventRef event2 = CGEventCreateCopy(event);
printf("%d\n", myRetainCount(event2)); // 1
printf("%d\n", CFGetRetainCount(event2)); // 1
}
all good here, no surprises.
Using the following helpers on the C side:
int myRetainCount(long param) {
return ((NSObject*)param).retainCount;
}
int myRetainCount2(long param) {
return CFGetRetainCount((CFTypeRef)param);
}
int myRetainCount3(CFTypeRef param) {
return CFGetRetainCount(param);
}
let's try it from swift:
print(myRetainCount(unsafeBitCast(event, to: Int.self))) // 1
print(myRetainCount2(unsafeBitCast(event, to: Int.self))) // 1
So far so good. Now:
print(CFGetRetainCount(event)) // 2
Oops. Passing event inside CFGetRetainCount affects its retain count. But's that only temporary:
print(myRetainCount2(unsafeBitCast(event, to: Int.self))) // 1, good again
Ditto:
print(myRetainCount3(event)) // 2, oops
print(myRetainCount2(unsafeBitCast(event, to: Int.self))) // 1, good again
By using the above "bit cast to int" trick I'm merely disabling all possible retain / release going under the hood when passing a ref-counted parameter inside a function, so it gives correct results.
"event.copy" is CGEventCreateCopy:
let c = event.copy()!
-> 0x100006f1c <+1544>: bl 0x10008f0b4 ; symbol stub for: CGEventCreateCopy
It returns +1 object like all "create" and "copy" calls within Apple ecosystem.