soranoba
soranoba Author of soranoba.net
programming

_UIResponderForwarderWantsForwardingFromResponderのクラッシュへの対応方法

iOS16になってから_UIResponderForwarderWantsForwardingFromResponderでクラッシュするケースが散見されるようになりました。
一例を上げると以下のようなクラッシュログのようなものが該当します。

Thread 0 name:
Thread 0 Crashed:
0   libsystem_kernel.dylib        	0x00000001c9d71574 __terminate_with_payload + 8
1   libsystem_kernel.dylib        	0x00000001c9d92c34 abort_with_payload_wrapper_internal + 136 (terminate_with_reason.c:106)
2   libsystem_kernel.dylib        	0x00000001c9d92c48 abort_with_payload + 16 (terminate_with_reason.c:124)
3   libsystem_c.dylib             	0x000000019258ac14 _os_crash_msg + 116 (assumes.c:285)
4   UIKitCore                     	0x000000018d1d3850 _UIResponderForwarderWantsForwardingFromResponder + 1256 (UITouch.m:240)
5   UIKitCore                     	0x000000018d0ebcbc __forwardTouchMethod_block_invoke + 44 (UIResponder.m:2171)
6   CoreFoundation                	0x000000018b053328 __NSSET_IS_CALLING_OUT_TO_A_BLOCK__ + 24 (NSSetHelpers.m:10)
7   CoreFoundation                	0x000000018b0d28f8 -[__NSSetM enumerateObjectsWithOptions:usingBlock:] + 200 (NSSetM_Common.h:157)
8   UIKitCore                     	0x000000018d2b47b8 forwardTouchMethod + 236 (UIResponder.m:2170)
9   UIKitCore                     	0x000000018d1b0938 -[UIWindow _sendTouchesForEvent:] + 356 (UIWindow.m:3170)
10  UIKitCore                     	0x000000018d1aff30 -[UIWindow sendEvent:] + 3280 (UIWindow.m:3491)
11  UIKitCore                     	0x000000018d1af1e4 -[UIApplication sendEvent:] + 676 (UIApplication.m:12655)
12  UIKitCore                     	0x000000018d1ad4d0 __dispatchPreprocessedEventFromEventQueue + 1768 (UIEventDispatcher.m:2414)
13  UIKitCore                     	0x000000018d1f6130 __processEventQueue + 5576 (UIEventDispatcher.m:2723)
14  UIKitCore                     	0x000000018d4fb114 __eventFetcherSourceCallback + 160 (UIEventDispatcher.m:2795)
15  CoreFoundation                	0x000000018b11c208 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 28 (CFRunLoop.c:1957)
16  CoreFoundation                	0x000000018b128864 __CFRunLoopDoSource0 + 176 (CFRunLoop.c:2001)
17  CoreFoundation                	0x000000018b0ad6c8 __CFRunLoopDoSources0 + 244 (CFRunLoop.c:2038)
18  CoreFoundation                	0x000000018b0c31c4 __CFRunLoopRun + 828 (CFRunLoop.c:2953)
19  CoreFoundation                	0x000000018b0c84dc CFRunLoopRunSpecific + 612 (CFRunLoop.c:3418)
20  GraphicsServices              	0x00000001c633435c GSEventRunModal + 164 (GSEvent.c:2196)
21  UIKitCore                     	0x000000018d45437c -[UIApplication _run] + 888 (UIApplication.m:3773)
22  UIKitCore                     	0x000000018d453fe0 UIApplicationMain + 340 (UIApplication.m:5363)
23  MobCas                        	0x0000000100300180 main + 64 (AppDelegate.swift:17)
24  dyld                          	0x00000001aa55cdec start + 2220 (dyldMain.cpp:1165)

クラッシュの原因

サードパーティ・ソフトウェアキーボードを使用している場合に、クラッシュする場合があるという物のようです。
SnowGirls氏が再現用のレポジトリを公開しています

対応方法

SnowGirls氏が対応方法をレポジトリ内で公開していますが、少し冗長なので整理した物で対応しました。
xxx_にしていますが、適当なアプリケーションプレフィックスを付けて利用してください)

@interface UITouch (UIResponderForwarderPatch)
@end
@implementation UITouch (UIResponderForwarderPatch)

+ (void)load {
    [super load];

    SEL sel = NSSelectorFromString(@"_wantsForwardingFromResponder:toNextResponder:withEvent:");
    Method originalMethod = class_getInstanceMethod(self, sel);
    if (originalMethod != NULL) {
        method_exchangeImplementations(originalMethod,
                                       class_getInstanceMethod(self, @selector(xxx_original_wantsForwardingFromResponder:toNextResponder:withEvent:)));
        method_exchangeImplementations(class_getInstanceMethod(self, @selector(xxx_wantsForwardingFromResponder:toNextResponder:withEvent:)),
                                       originalMethod);
    }
}

- (BOOL)xxx_wantsForwardingFromResponder:(UIResponder *)arg1 toNextResponder:(UIResponder *)arg2 withEvent:(UIEvent *)arg3 {
    NSString* responderClassName = NSStringFromClass([arg2 class]);
    if ([responderClassName isEqualToString:@"_UIRemoteInputViewController"]) {
        BOOL isDeallocating = NO;

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        SEL sel = NSSelectorFromString(@"_isDeallocating");
        isDeallocating = [arg2 respondsToSelector:sel] && [arg2 performSelector:sel];
#pragma clang diagnostic pop

        if (isDeallocating) {
            return YES;
        }
    }
    return [self xxx_original_wantsForwardingFromResponder:arg1 toNextResponder:arg2 withEvent:arg3];
}

- (BOOL)xxx_original_wantsForwardingFromResponder:(UIResponder *)arg1 toNextResponder:(UIResponder *)arg2 withEvent:(UIEvent *)arg3 {
    return NO;
}

@end

(Updated: )

comments powered by Disqus