Skip to content

Instantly share code, notes, and snippets.

@DmitrySkiba
Created October 1, 2013 07:32
Show Gist options
  • Save DmitrySkiba/6774995 to your computer and use it in GitHub Desktop.
Save DmitrySkiba/6774995 to your computer and use it in GitHub Desktop.
Proof that CFGetTypeID() doesn't call [_cfTypeID]
#include <CoreFoundation/CoreFoundation.h>
#include <objc/runtime.h>
typedef struct __CFRuntimeClass {
CFIndex version;
const char *className;
void (*init)(CFTypeRef cf);
CFTypeRef (*copy)(CFAllocatorRef allocator, CFTypeRef cf);
void (*finalize)(CFTypeRef cf);
Boolean (*equal)(CFTypeRef cf1, CFTypeRef cf2);
CFHashCode (*hash)(CFTypeRef cf);
CFStringRef (*copyFormattingDesc)(CFTypeRef cf, CFDictionaryRef formatOptions); // str with retain
CFStringRef (*copyDebugDesc)(CFTypeRef cf); // str with retain
void (*reclaim)(CFTypeRef cf);
} CFRuntimeClass;
typedef struct __CFRuntimeBase {
uintptr_t _cfisa;
uint8_t _cfinfo[4];
#if __LP64__
uint32_t _rc;
#endif
} CFRuntimeBase;
extern CFTypeID _CFRuntimeRegisterClass(const CFRuntimeClass* const cls);
extern CFTypeRef _CFRuntimeCreateInstance(CFAllocatorRef allocator,
CFTypeID typeID, CFIndex extraBytes,
unsigned char* category);
// =============================================== _cfTypeID swizzling
static BOOL CFTypeIDCalled = NO;
static IMP OriginalCFTypeID = NULL;
static CFTypeID MyCFTypeID(id self, SEL _cmd)
{
CFTypeIDCalled = YES;
return ((CFTypeID(*)(id,SEL))OriginalCFTypeID)(self, _cmd);
}
static void SwizzleCFTypeID(Class class, BOOL swizzle)
{
Method method = class_getInstanceMethod(class, @selector(_cfTypeID));
NSCAssert(method, @"There is no _cfTypeID in %s", class_getName(class));
if (swizzle)
{
OriginalCFTypeID = method_getImplementation(method);
method_setImplementation(method, (IMP)&MyCFTypeID);
}
else
{
method_setImplementation(method, OriginalCFTypeID);
OriginalCFTypeID = NO;
}
}
// =============================================== tests
@interface NSObject (_cfTypeID)
- (CFTypeID)_cfTypeID;
@end
static void TestCFGetTypeID(CFTypeRef object, CFTypeID expectedTypeID)
{
Class class = (Class)((const CFRuntimeBase*)object)->_cfisa;
SwizzleCFTypeID(class, YES);
CFTypeIDCalled = NO;
CFTypeID typeID = CFGetTypeID(object);
NSCAssert(typeID == expectedTypeID, @"CFGetTypeID() returned bad type ID %ld (expected %ld)",
typeID, expectedTypeID);
NSCAssert(!CFTypeIDCalled, @"_cfTypeID was called during CFGetTypeID() call on %p of objc type %s",
object, class_getName(class));
CFTypeIDCalled = NO;
[(id)object _cfTypeID];
NSCAssert(CFTypeIDCalled, @"_cfTypeID was NOT called during [_cfTypeID] message to %p of objc type %s",
object, class_getName(class));
SwizzleCFTypeID(class, NO);
}
typedef struct
{
CFRuntimeBase base;
} MyObject;
static CFRuntimeClass MyObjectClass = {
0,
"MyObject"
};
void RunTests(void)
{
// Custom object
{
CFTypeID myTypeID = _CFRuntimeRegisterClass(&MyObjectClass);
NSCAssert(myTypeID != 0, @"Failed to register custom CF class");
MyObject* object = (MyObject*)_CFRuntimeCreateInstance(
kCFAllocatorDefault,
myTypeID,
sizeof(MyObject) - sizeof(CFRuntimeBase),
NULL);
TestCFGetTypeID(object, myTypeID);
CFRelease(object);
}
// CFString
{
CFStringRef string = (CFStringRef)@"test";
TestCFGetTypeID(string, CFStringGetTypeID());
}
// CFArray
{
CFArrayRef array = CFArrayCreate(kCFAllocatorDefault, NULL, 0, NULL);
TestCFGetTypeID(array, CFArrayGetTypeID());
CFRelease(array);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment