Created
August 7, 2019 08:59
-
-
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.
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
// 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); | |
} |
Hi there! We don’t use it for struct returns currently so this is an unused code path. There’s an internal ticket for adding these edge cases - if you have time, would love to see your take on it (you can fork gists)
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
Does PSPDF team actually use this snippet for swizzling? On arm64 platform, if the original method returns a large c struct, this implementation will use
objc_msgSendSuper2
instead ofobjc_msgSendSuper2_stret
. There may be more cases we need to consider.