Created
October 1, 2013 07:32
-
-
Save DmitrySkiba/6774995 to your computer and use it in GitHub Desktop.
Proof that CFGetTypeID() doesn't call [_cfTypeID]
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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