美文网首页
iOS11方法混淆导致tableView edit Cell异常

iOS11方法混淆导致tableView edit Cell异常

作者: coderPoo | 来源:发表于2018-07-03 18:29 被阅读11次
    一.先放问题现象
    • 会话列表,有编辑功能,无编辑状态是下图


      image.png
    • 正常的编辑状态


      image.png
    • 异常的编辑状态 ,编辑控件占据两行


      image.png
    • 左动第1行时,编辑状态确在第8行显示 (以上问题大部分出现在列表超过一屏的情况下)
    二.凭借祖传的开发经验猜测问题所在

    首先想到的是不是iOS11的 tableView 新方法的问题,或者是开发中使用有问题

    // Swipe actions
    // These methods supersede -editActionsForRowAtIndexPath: if implemented
    // return nil to get the default swipe actions
    - (nullable UISwipeActionsConfiguration *)tableView:(UITableView *)tableView leadingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos);
    - (nullable UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos);
    

    项目中的代码

    - (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath API_AVAILABLE(ios(11.0))
    {
       if (indexPath.row >= self.chatData.count)
           return nil;
       
       GZIMChatModel *chatModel = self.chatData[indexPath.row];
       WS(weakSelf);
       UIContextualAction *deleteAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleDestructive title:@"删除" handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
           [weakSelf p_deleteChat:chatModel completeHandler:completionHandler];
       }];
       UIContextualAction *topRowAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleNormal title:(chatModel.chat.isTop?@"取消\n置顶":@"置顶") handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
           [weakSelf p_changeTopStatusWithChat:chatModel completeHandler:completionHandler];
       }];
       UIContextualAction *disturbRowAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleNormal title:(chatModel.chat.unreadCount?@"标为\n已读":@"标为\n未读") handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
           [weakSelf p_changeDisturbStatusWithChat:chatModel completeHandler:completionHandler];
       }];
       disturbRowAction.backgroundColor = [UIColor lightGrayColor];
       
       UISwipeActionsConfiguration *config;
       if (chatModel.chat.chatType==GZIMChatType_Subscription) {
           config = [UISwipeActionsConfiguration configurationWithActions:@[deleteAction]];
       } else {
           config = [UISwipeActionsConfiguration configurationWithActions:@[deleteAction,topRowAction,disturbRowAction]];
       }
       config.performsFirstActionWithFullSwipe = NO;
       return config;
    }
    

    google查阅很多网上资料使用方式基本没有问题,而且网上也没有类似的问题出现

    三、 分析项目多个版本以及最近业务逻辑

    通过sourceTree代码回到上一个版本的tag位置,发现没有类似问题
    基本能确定这是这一版本出现的问题
    最后发现因为项目中最近加了 防止数组越界或者字典传nil 的代码混淆机制导致这个问题

    @implementation NSMutableDictionary (NilSafe)
    
    + (void)load
    {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            Class class = NSClassFromString(@"__NSDictionaryM");
            [class exchangeInstanceMethod:@selector(setObject:forKey:) swizzleMethod:@selector(safeSetObject:forKey:)];
            //问题出现在这个上
            [class exchangeInstanceMethod:@selector(setObject:forKeyedSubscript:) swizzleMethod:@selector(safeSetObject:forKeyedSubscript:)];
            [class exchangeInstanceMethod:@selector(removeObjectForKey:) swizzleMethod:@selector(safeRemoveObjectForKey:)];
        });
    }
    
    - (void)safeSetObject:(id)anObject forKey:(id<NSCopying>)aKey {
        if (!aKey || !anObject) {
            XLOG_INFO(@"NSMutableDictionary setObjectForKey aKey or anObject is nil");
            return;
        }
        [self safeSetObject:anObject forKey:aKey];
    }
    
    - (void)safeSetObject:(id)obj forKeyedSubscript:(id<NSCopying>)key {
        if (!key || !obj) {
            XLOG_INFO(@"NSMutableDictionary setObjectForKeyedSubscript aKey is: %@ or anObject is %@",key,obj);
            return;
        }
        XLOG_INFO(@"NSMutableDictionary aKey is: %@ and anObject is:%@",key,obj);
        [self safeSetObject:obj forKeyedSubscript:key];
    }
    
    - (void)safeRemoveObjectForKey:(id)aKey
    {
        if (!aKey) {
            XLOG_INFO(@"NSMutableDictionary removeObjectForKey aKey is nil");
            return;
        }
        return [self safeRemoveObjectForKey:aKey];
    }
    
    @end
    

    左滑编辑时的 正常的日志

    2018-07-03 18:25:19.121977+0800 GZIMClient[30512:2773379] [I][GZIM][NSObject+Swizzling.m, -[NSMutableDictionary(NilSafe) safeSetObject, 247][Info:NSMutableDictionary aKey is: <NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0} and anObject is:<UISwipeOccurrence: 0x6040002f4200>

    左滑编辑时的 异常的日志

    2018-07-03 18:25:20.076750+0800 GZIMClient[30512:2773379] [I][GZIM][NSObject+Swizzling.m, -[NSMutableDictionary(NilSafe) safeSetObject, 244][Info:NSMutableDictionary setObjectForKeyedSubscript aKey is: <NSIndexPath: 0xc000000000800016> {length = 2, path = 0 - 4} or anObject is (null)

    说明iOS11之后 新编辑方法用到了 下边这个方法

    - (void)setObject:(nullable ObjectType)obj forKeyedSubscript:(KeyType <NSCopying>)key API_AVAILABLE(macos(10.8), ios(6.0), watchos(2.0), tvos(9.0));
    

    而我们又代码混淆了这个方法,导致了问题的出现,应该是混淆导致 UISwipeOccurrence没有传入,不能正确获取,导致错位
    解决方法: 只能先注销到这段方法混淆

    四、 疑问

    不明白为什么会出现这种问题,应该是内部原理

    相关文章

      网友评论

          本文标题:iOS11方法混淆导致tableView edit Cell异常

          本文链接:https://www.haomeiwen.com/subject/wvyjuftx.html