Skip to content

Instantly share code, notes, and snippets.

@steipete
Created August 7, 2019 08:59
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save steipete/1d308fad786399b58875cd12e4b9bba2 to your computer and use it in GitHub Desktop.
Save steipete/1d308fad786399b58875cd12e4b9bba2 to your computer and use it in GitHub Desktop.
pspdf_swizzleSelectorWithBlock, pspdf_swizzleSelector. Please use your own prefix when you copy these.
// http://defagos.github.io/yet_another_article_about_method_swizzling/ (Thank you!!)
// Returns the original implementation
static _Nullable IMP pspdf_swizzleSelector(Class clazz, SEL selector, IMP newImplementation) {
NSCParameterAssert(clazz);
NSCParameterAssert(selector);
NSCParameterAssert(newImplementation);
// If the method does not exist for this class, do nothing.
const Method method = class_getInstanceMethod(clazz, selector);
if (!method) {
PSPDFLogError(@"%@ doesn't exist in %@.", NSStringFromSelector(selector), NSStringFromClass(clazz));
// 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);
@synchronized(clazz) {
// class_addMethod will simply return NO if the method is already implemented.
#if !defined(__arm64__)
// Sufficiently large struct
typedef struct LargeStruct_ { char dummy[16]; } LargeStruct;
NSUInteger retSize = 0;
NSGetSizeAndAlignment(types, &retSize, NULL);
// Large structs on 32-bit architectures
// TODO: This is incorrect for some structs on some architectures. Needs to be hardcoded, this cannot be safely inferred at runtime.
// https://twitter.com/gparker/status/1028564412339113984
if (sizeof(void *) == 4 && types[0] == _C_STRUCT_B && retSize != 1 && retSize != 2 && retSize != 4 && retSize != 8) {
class_addMethod(clazz, selector, imp_implementationWithBlock(^(__unsafe_unretained id self, va_list argp) {
struct objc_super super = {self, clazz};
return ((LargeStruct(*)(struct objc_super *, SEL, va_list))objc_msgSendSuper2_stret)(&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 = {self, clazz};
return ((id(*)(struct objc_super *, SEL, va_list))objc_msgSendSuper2)(&super, selector, argp);
}), types);
#if !defined(__arm64__)
}
#endif
// Swizzling
return class_replaceMethod(clazz, selector, newImplementation, types);
}
}
_Nullable IMP pspdf_swizzleSelectorWithBlock(Class clazz, SEL selector, id newImplementationBlock) {
const IMP newImplementation = imp_implementationWithBlock(newImplementationBlock);
return pspdf_swizzleSelector(clazz, selector, newImplementation);
}
@zetasq
Copy link

zetasq commented Dec 2, 2019

Although clang's implementation (https://github.com/llvm-mirror/clang/blob/a782b2b93cd493f514a5ed77721b7a361bc91553/lib/CodeGen/CGObjCMac.cpp) may give us guidelines about which objc_msg function should be used, the casting to variadic function prototype ((id(*)(struct objc_super *, SEL, va_list)) is a trickier issue seems hard to solve (https://mikeash.com/pyblog/objc_msgsends-new-prototype.html). It seems that the designers of objective-c really don't want us to use objc_msg functions directly :(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment