iOS14+下BlocksKit崩溃

iOS 14开始,输入时使用系统的emoji键盘时,使用键盘自带的搜索框TUIEmojiSearchTextField,会导致崩溃。

表情键盘

堆栈中注意到来自TUIEmojiSearchTextField的调用,点击emoji,就会崩溃。

崩溃信息

从堆栈看是无限循环调用keyboardInputChangedSelection:最终导致崩溃,崩溃位置显示在A2DynamicDelegate.m中。

崩溃

方法一

NSObject+A2BlockDelegate.mbk_registerDynamicDelegateNamed:方法中过滤掉TUIEmojiSearchTextField

//添加如下代码
 if (@available(iOS 13.0, *)) {
        if ([delegate isKindOfClass:NSClassFromString(@"TUIEmojiSearchTextField")]) {
             delegate = nil;
      }
 }

+ (void)bk_registerDynamicDelegateNamed:(NSString *)delegateName forProtocol:(Protocol *)protocol
{
    NSMapTable *propertyMap = [self bk_delegateInfoByProtocol:YES];
    A2BlockDelegateInfo *infoAsPtr = (__bridge void *)[propertyMap objectForKey:protocol];
    if (infoAsPtr != NULL) { return; }
    
    const char *name = delegateName.UTF8String;
    objc_property_t property = class_getProperty(self, name);
    SEL setter = setterForProperty(property, name);
    SEL a2_setter = prefixedSelector(setter);
    SEL getter = getterForProperty(property, name);
    
    A2BlockDelegateInfo info = {
        setter, a2_setter, getter
    };
    
    [propertyMap setObject:(__bridge id)&info forKey:protocol];
    infoAsPtr = (__bridge void *)[propertyMap objectForKey:protocol];
    
    IMP setterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject, id delegate) {
        A2DynamicDelegate *dynamicDelegate = getDynamicDelegate(delegatingObject, protocol, infoAsPtr, YES);
        if ([delegate isEqual:dynamicDelegate]) {
            delegate = nil;
        }
        if (@available(iOS 13.0, *)) {
            if ([delegate isKindOfClass:NSClassFromString(@"TUIEmojiSearchTextField")]) {
                delegate = nil;
            }
        }
        dynamicDelegate.realDelegate = delegate;
    });
    
    if (!swizzleWithIMP(self, setter, a2_setter, setterImplementation, "v@:@", YES)) {
        bzero(infoAsPtr, sizeof(A2BlockDelegateInfo));
        return;
    }
    
    if (![self instancesRespondToSelector:getter]) {
        IMP getterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject) {
            return [delegatingObject bk_dynamicDelegateForProtocol:a2_protocolForDelegatingObject(delegatingObject, protocol)];
        });
        
        addMethodWithIMP(self, getter, NULL, getterImplementation, "@@:", NO);
    }
}

方法二

修改UITextField+BlocksKit.m处理TUIEmojiSearchTextField

//
//  UITextField+BlocksKit.m
//  BlocksKit
//

#import "UITextField+BlocksKit.h"
#import "A2DynamicDelegate.h"
#import "NSObject+A2BlockDelegate.h"

#pragma mark Delegate

@interface A2DynamicUITextFieldDelegate : A2DynamicDelegate

@end

@implementation A2DynamicUITextFieldDelegate

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
    BOOL ret = YES;
    id realDelegate = self.realDelegate;
    if (realDelegate && [realDelegate respondsToSelector:@selector(textFieldShouldBeginEditing:)])
        ret = [realDelegate textFieldShouldBeginEditing:textField];
    BOOL (^block)(UITextField *) = [self blockImplementationForMethod:_cmd];
    if (block)
        ret &= block(textField);
    return ret;
}

- (void)textFieldDidBeginEditing:(UITextField *)textField {
    id realDelegate = self.realDelegate;
    if (realDelegate && [realDelegate respondsToSelector:@selector(textFieldDidBeginEditing:)])
        [realDelegate textFieldDidBeginEditing:textField];
    void (^block)(UITextField *) = [self blockImplementationForMethod:_cmd];
    if (block)
        block(textField);
}

- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
    BOOL ret = YES;
    id realDelegate = self.realDelegate;
    if (realDelegate && [realDelegate respondsToSelector:@selector(textFieldShouldEndEditing:)])
        ret = [realDelegate textFieldShouldEndEditing:textField];
    BOOL (^block)(UITextField *) = [self blockImplementationForMethod:_cmd];
    if (block)
        ret &= block(textField);
    return ret;
}

- (void)textFieldDidEndEditing:(UITextField *)textField {
    id realDelegate = self.realDelegate;
    if (realDelegate && [realDelegate respondsToSelector:@selector(textFieldDidEndEditing:)])
        [realDelegate textFieldDidEndEditing:textField];
    void (^block)(UITextField *) = [self blockImplementationForMethod:_cmd];
    if (block)
        block(textField);
}

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    BOOL ret = YES;
    id realDelegate = self.realDelegate;
    if (realDelegate && [realDelegate respondsToSelector:@selector(textField:shouldChangeCharactersInRange:replacementString:)])
        ret = [realDelegate textField:textField shouldChangeCharactersInRange:range replacementString:string];
    BOOL (^block)(UITextField *, NSRange, NSString *) = [self blockImplementationForMethod:_cmd];
    if (block)
        ret &= block(textField, range, string);
    return ret;
}

- (BOOL)textFieldShouldClear:(UITextField *)textField {
    BOOL ret = YES;
    id realDelegate = self.realDelegate;
    if (realDelegate && [realDelegate respondsToSelector:@selector(textFieldShouldClear:)])
        ret = [realDelegate textFieldShouldClear:textField];
    BOOL (^block)(UITextField *) = [self blockImplementationForMethod:_cmd];
    if (block)
        ret &= block(textField);
    return ret;
}

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    BOOL ret = YES;
    id realDelegate = self.realDelegate;
    if (realDelegate && [realDelegate respondsToSelector:@selector(textFieldShouldReturn:)])
        ret = [realDelegate textFieldShouldReturn:textField];
    BOOL (^block)(UITextField *) = [self blockImplementationForMethod:_cmd];
    if (block)
        ret &= block(textField);
    return ret;
}

- (BOOL)keyboardInputChangedSelection:(UITextField *)textField {
    BOOL ret = YES;
    id realDelegate = self.realDelegate;
    if ([textField isKindOfClass:NSClassFromString(@"TUIEmojiSearchTextField")]) {
        return ret;
    }
    if (realDelegate && [realDelegate respondsToSelector:@selector(keyboardInputChangedSelection:)])
        ret = [realDelegate keyboardInputChangedSelection:textField];
    BOOL (^block)(UITextField *) = [self blockImplementationForMethod:_cmd];
    if (block)
        ret &= block(textField);
    return ret;
}

- (BOOL)keyboardInputChanged:(UITextField *)textField {
    BOOL ret = YES;
    id realDelegate = self.realDelegate;
    if ([textField isKindOfClass:NSClassFromString(@"TUIEmojiSearchTextField")]) {
        return ret;
    }
    if (realDelegate && [realDelegate respondsToSelector:@selector(keyboardInputChanged:)])
        ret = [realDelegate keyboardInputChanged:textField];
    BOOL (^block)(UITextField *) = [self blockImplementationForMethod:_cmd];
    if (block)
        ret &= block(textField);
    return ret;
}

- (BOOL)keyboardInputShouldDelete:(UITextField *)textField {
    BOOL ret = YES;
    id realDelegate = self.realDelegate;
    if ([textField isKindOfClass:NSClassFromString(@"TUIEmojiSearchTextField")]) {
        return ret;
    }
    if (realDelegate && [realDelegate respondsToSelector:@selector(keyboardInputShouldDelete:)])
        ret = [realDelegate keyboardInputShouldDelete:textField];
    BOOL (^block)(UITextField *) = [self blockImplementationForMethod:_cmd];
    if (block)
        ret &= block(textField);
    return ret;
}

@end

#pragma mark - Category

@implementation UITextField (BlocksKit)

@dynamic bk_shouldBeginEditingBlock, bk_didBeginEditingBlock, bk_shouldEndEditingBlock, bk_didEndEditingBlock, bk_shouldChangeCharactersInRangeWithReplacementStringBlock, bk_shouldClearBlock, bk_shouldReturnBlock;

+ (void)load {
    [self bk_registerDynamicDelegate];
    [self bk_linkDelegateMethods:@{
            @"bk_shouldBeginEditingBlock": @"textFieldShouldBeginEditing:",
            @"bk_didBeginEditingBlock": @"textFieldDidBeginEditing:",
            @"bk_shouldEndEditingBlock": @"textFieldShouldEndEditing:",
            @"bk_didEndEditingBlock": @"textFieldDidEndEditing:",
            @"bk_shouldChangeCharactersInRangeWithReplacementStringBlock": @"textField:shouldChangeCharactersInRange:replacementString:",
            @"bk_shouldClearBlock": @"textFieldShouldClear:",
            @"bk_shouldReturnBlock": @"textFieldShouldReturn:",
    }];
}

- (id)customOverlayContainer {
    return self;
}
@end

你可能感兴趣的:(iOS14+下BlocksKit崩溃)