Last active
September 5, 2024 01:31
-
-
Save defagos/1312fec96b48540efa5c to your computer and use it in GitHub Desktop.
Method swizzling implementation (see http://defagos.github.io/yet_another_article_about_method_swizzling/ for details)
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
@implementation UILabel (SwizzlingExamples) | |
+ (void)load | |
{ | |
SwizzleSelectorWithBlock_Begin(self, @selector(initWithFrame:)) | |
^(UILabel *self, CGRect frame) { | |
if ((self = ((id (*)(id, SEL, CGRect))_imp)(self, _cmd, frame))) { | |
// ... | |
} | |
return self; | |
} | |
SwizzleSelectorWithBlock_End; | |
SwizzleSelectorWithBlock_Begin(self, @selector(awakeFromNib)) | |
^(UILabel *self) { | |
((void (*)(id, SEL))_imp)(self, _cmd); | |
// ... | |
} | |
SwizzleSelectorWithBlock_End; | |
SwizzleSelectorWithBlock_Begin(self, sel_getUid("dealloc")) | |
^(__unsafe_unretained UILabel *self) { | |
// ... | |
((void (*)(id, SEL))_imp)(self, _cmd); | |
} | |
SwizzleSelectorWithBlock_End; | |
} | |
@end |
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
static id (*initWithFrame)(id, SEL, CGRect) = NULL; | |
static void (*awakeFromNib)(id, SEL) = NULL; | |
static void (*dealloc)(__unsafe_unretained id, SEL) = NULL; | |
static id swizzle_initWithFrame(UILabel *self, SEL _cmd, CGRect frame) | |
{ | |
if ((self = initWithFrame(self, _cmd, frame))) { | |
// ... | |
} | |
return self; | |
} | |
static void swizzle_awakeFromNib(UILabel *self, SEL _cmd) | |
{ | |
awakeFromNib(self, _cmd); | |
// ... | |
} | |
static void swizzle_dealloc(__unsafe_unretained UILabel *self, SEL _cmd) | |
{ | |
// ... | |
dealloc(self, _cmd); | |
} | |
@implementation UILabel (SwizzlingExamples) | |
+ (void)load | |
{ | |
SwizzleSelector(self, @selector(initWithFrame:), swizzle_initWithFrame, &initWithFrame); | |
SwizzleSelector(self, @selector(awakeFromNib), &swizzle_awakeFromNib, &awakeFromNib); | |
SwizzleSelector(self, sel_getUid("dealloc"), &swizzle_dealloc, &dealloc); | |
} | |
@end |
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
#import <objc/runtime.h> | |
#import <objc/message.h> | |
#define SwizzleSelector(clazz, selector, newImplementation, pPreviousImplementation) \ | |
(*pPreviousImplementation) = (__typeof((*pPreviousImplementation)))class_swizzleSelector((clazz), (selector), (IMP)(newImplementation)) | |
#define SwizzleClassSelector(clazz, selector, newImplementation, pPreviousImplementation) \ | |
(*pPreviousImplementation) = (__typeof((*pPreviousImplementation)))class_swizzleClassSelector((clazz), (selector), (IMP)(newImplementation)) | |
#define SwizzleSelectorWithBlock_Begin(clazz, selector) { \ | |
SEL _cmd = selector; \ | |
__block IMP _imp = class_swizzleSelectorWithBlock((clazz), (selector), | |
#define SwizzleSelectorWithBlock_End );} | |
#define SwizzleClassSelectorWithBlock_Begin(clazz, selector) { \ | |
SEL _cmd = selector; \ | |
__block IMP _imp = class_swizzleClassSelectorWithBlock((clazz), (selector), | |
#define SwizzleClassSelectorWithBlock_End );} | |
IMP class_swizzleSelector(Class clazz, SEL selector, IMP newImplementation) | |
{ | |
// If the method does not exist for this class, do nothing | |
Method method = class_getInstanceMethod(clazz, selector); | |
if (! method) { | |
// Cannot swizzle methods which are not implemented by the class or one of its parents | |
return NULL; | |
} | |
// Make sure the class implements the method. If this is not the case, inject an implementation, only calling 'super' | |
const char *types = method_getTypeEncoding(method); | |
#if !defined(__arm64__) | |
NSUInteger returnSize = 0; | |
NSGetSizeAndAlignment(types, &returnSize, NULL); | |
// Large structs on 32-bit architectures | |
if (sizeof(void *) == 4 && types[0] == _C_STRUCT_B && returnSize != 1 && returnSize != 2 && returnSize != 4 && returnSize != 8) { | |
class_addMethod(clazz, selector, imp_implementationWithBlock(^(__unsafe_unretained id self, va_list argp) { | |
struct objc_super super = { | |
.receiver = self, | |
.super_class = class_getSuperclass(clazz) | |
}; | |
// Sufficiently large struct | |
typedef struct LargeStruct_ { | |
char dummy[16]; | |
} LargeStruct; | |
// Cast the call to objc_msgSendSuper_stret appropriately | |
LargeStruct (*objc_msgSendSuper_stret_typed)(struct objc_super *, SEL, va_list) = (void *)&objc_msgSendSuper_stret; | |
return objc_msgSendSuper_stret_typed(&super, selector, argp); | |
}), types); | |
} | |
// All other cases | |
else { | |
#endif | |
class_addMethod(clazz, selector, imp_implementationWithBlock(^(__unsafe_unretained id self, va_list argp) { | |
struct objc_super super = { | |
.receiver = self, | |
.super_class = class_getSuperclass(clazz) | |
}; | |
// Cast the call to objc_msgSendSuper appropriately | |
id (*objc_msgSendSuper_typed)(struct objc_super *, SEL, va_list) = (void *)&objc_msgSendSuper; | |
return objc_msgSendSuper_typed(&super, selector, argp); | |
}), types); | |
#if !defined(__arm64__) | |
} | |
#endif | |
// Swizzling | |
return class_replaceMethod(clazz, selector, newImplementation, types); | |
} | |
IMP class_swizzleClassSelector(Class clazz, SEL selector, IMP newImplementation) | |
{ | |
return class_swizzleSelector(object_getClass(clazz), selector, newImplementation); | |
} | |
IMP class_swizzleSelectorWithBlock(Class clazz, SEL selector, id newImplementationBlock) | |
{ | |
IMP newImplementation = imp_implementationWithBlock(newImplementationBlock); | |
return class_swizzleSelector(clazz, selector, newImplementation); | |
} | |
IMP class_swizzleClassSelectorWithBlock(Class clazz, SEL selector, id newImplementationBlock) | |
{ | |
IMP newImplementation = imp_implementationWithBlock(newImplementationBlock); | |
return class_swizzleClassSelector(clazz, selector, newImplementation); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
How does having
va_list
as an argument instead of...
work? And how does callingobjc_msgSendSuper
with ava_list
as the last argument expand into the full arguments within? Doesn't that just pass theva_list
as an argument to the final Objective-C function?