iOS11中UITableViewCell不能侧滑编辑的诡异问题

2017-12-08

组员反馈项目中有一个诡异的问题,在iOS11中,工程中所有页面的UITableViewCell第一次能侧滑编辑,只要停留在该页面,之后再也无法侧滑编辑,但是仍旧能断点到代理方法中。
当时第一反应是怀疑工程中大量的Swizzle方法,庞大的工程也不知从何查起,抱着运行项目看看日志提示,碰碰运气的心态run了项目跑在模拟器中,在第一次侧滑编辑时无异样,滚屏退出编辑态,第二次侧滑时,tableview就无法应了,但好在日志中发现了痕迹:

1
*** NSForwarding: warning: method signature and compiler disagree on struct-return-edness of 'prepareWithSwipeDirection:configuration:'.  Signature thinks it does not return a struct, and compiler thinks it does.

大致意思是方法签名描述不一致,也就是约定返回一个struct的,但并没有返回struct类型。

习惯性的去Google上查找资料发现链接
在其中找到了__forwarding__的分析代码为:

1
2
3
4
5
6
7
8
BOOL signatureIsStret = [methodSignature _frameDescriptor]->returnArgInfo.flags.isStruct;
if (signatureIsStret != isStret) {
CFLog(kCFLogLevelWarning ,
@"*** NSForwarding: warning: method signature and compiler disagree on struct-return-edness of '%s'. Signature thinks it does%s return a struct, and compiler thinks it does%s.",
selName,
signatureIsStret ? "" : not,
isStret ? "" : not);
}

更加怀疑是项目中swizzle的方法惹的祸了

怀疑的步骤为:

  1. 先怀疑NSObject的methodSignatureForSelector方法,屏蔽掉工程中Swizzle的代码,发现并没解决问题。
  2. 怀疑之前解决数组和字典的nil问题,屏蔽掉所有数组和字典的swizzle方法,发现问题解决。
  3. 一个个大块放开代码检查,最终定位。

罪魁祸首:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@implementation NSMutableDictionary (NilSafe)

+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = NSClassFromString(@"__NSDictionaryM");
[class gl_swizzleMethod:@selector(setObject:forKeyedSubscript:) withMethod:@selector(gl_setObject:forKeyedSubscript:)];
});
}

- (void)gl_setObject:(id)obj forKeyedSubscript:(id<NSCopying>)key {
if (!key) {
return;
}
if (!obj) {
obj = [NSNull null];
}
[self gl_setObject:obj forKeyedSubscript:key];
}

@end

拦截了系统的字典,将obj为nil时替换成了NSNull,怀疑是组员从网上拷贝的代码来解决服务端下发数据中有nil的问题。如果是为了解决数组、字典中nil数据导致的crash问题,那判断到!obj时,直接return就好了。

最终解决方案:

1
2
3
4
5
6
7
if (!obj) {
obj = [NSNull null];
}
// 更改为
if (!obj) {
return;
}

个人觉得NSNull尽量少用,它一般用在闭环逻辑的代码中,也就是自己能掌控的闭环逻辑中,用它来标记、处理空逻辑行为。