Skip to content

Instantly share code, notes, and snippets.

@steipete
Last active December 5, 2018 19:47
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save steipete/08974d7544d328fb7978c10505834e00 to your computer and use it in GitHub Desktop.
Save steipete/08974d7544d328fb7978c10505834e00 to your computer and use it in GitHub Desktop.
If you get an [NSProxy doesNotRecognizeSelector:_accessibilityLoadAccessibilityInformation] crash in iOS 12, here's a temporary fix for your tests. Please change the prefix before you use this! MIT licensed.
static void PSPDFFixiOS12AccessibilityTestCrash(void) {
let accessibilityLoaderClass = NSClassFromString(@"UIAccessibilityInformationLoader");
let accessibilitySEL = NSSelectorFromString(@"_loadAccessibilityInformationOnMainThread:");
__block IMP originalIMP = pspdf_swizzleSelectorWithBlock(accessibilityLoaderClass, accessibilitySEL, ^(id _self, BOOL onMainThread) {
@try {
((void (*)(id, SEL, BOOL))originalIMP)(_self, accessibilitySEL, onMainThread);
} @catch (NSException *exception) {
NSLog(@"Exception received: %@", exception);
if ([exception.name isEqualToString:NSInvalidArgumentException] && [exception.reason containsString:@"_accessibilityLoadAccessibilityInformation"]) {
NSLog(@"Ignoring IOS 12b5 weirdness...");
} else {
// Rethrow other things
@throw exception;
}
}
});
}
// If you don't yet have such swizzling helpers, here's ours:
// Extracted straight from the ObjC Runtime headers:
// https://opensource.apple.com/source/objc4/objc4-493.9/runtime/objc-abi.h
//
// objc_msgSendSuper2() takes the current search class, not its superclass.
OBJC_EXPORT id objc_msgSendSuper2(struct objc_super *super, SEL op, ...)
__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT void objc_msgSendSuper2_stret(struct objc_super *super, SEL op,...)
__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
// http://defagos.github.io/yet_another_article_about_method_swizzling/
// Returns the original implementation
PSPDF_EXTERN _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);
}
}
PSPDF_EXTERN _Nullable IMP pspdf_swizzleSelectorWithBlock(Class clazz, SEL selector, id newImplementationBlock) {
const IMP newImplementation = imp_implementationWithBlock(newImplementationBlock);
return pspdf_swizzleSelector(clazz, selector, newImplementation);
}
@steipete
Copy link
Author

Filed as rdar://43228744 http://openradar.appspot.com/43228744

@steipete
Copy link
Author

Update: This was our bug. We use a multicast delegate internally, which missed to generate the correct method signature for a category loaded via UIAccessibility onto NSObject. This was fixed by forwarding to the delegate's implementation if our protocol cache missed the entry.

@felixbupt
Copy link

felixbupt commented Sep 21, 2018

Update: This was our bug. We use a multicast delegate internally, which missed to generate the correct method signature for a category loaded via UIAccessibility onto NSObject. This was fixed by forwarding to the delegate's implementation if our protocol cache missed the entry.

Do you mean that you have fixed the bug without hook the method? Can you tell me how to?
Thanks a lot!

@steipete
Copy link
Author

Yes, we fixed the bug by fixing our proxy forwarder logic. The 3-step process from respondsToSelector, methodSignature and forwardInvocation needs to be consistent for any call - even unexpected ones. We use a pre-built cache to speed up lookups and didn't consider the case where unexpected calls might be sent - especially for custom Apple-internal object categories that might skip the respondsToSelector part.

@gpambrozio
Copy link

@steipete I'm getting this error in a production app using PSPDFKit 7.6.2. Was this fixed on a later release? Thanks.

@steipete
Copy link
Author

steipete commented Dec 5, 2018

This has been fixed in PSPDFKit 7.7.2 for iOS.

Here's the detailed changelog: https://pspdfkit.com/changelog/ios/#7.7.2

See: Fixes an issue where iOS 12 could throw an exception inside -[UIAccessibilityInformationLoader _loadAccessibilityInformationOnMainThread:]. (#16489)

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