类型判断上的小技巧

时间:2019-10-17
本文章向大家介绍类型判断上的小技巧,主要包括类型判断上的小技巧使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

不知道大伙儿有没有这样的经历:
每次看到

1
-[__NSCFConstantString objectForKey:]: unrecognized selector sent to instance 0x1050a6be8
1
-[__NSCFNumber isEqualToString:]: unrecognized selector sent to instance 0xb0000000000007b2

这样的crash是不是会有种想死的感觉。不管怎样,我每次碰到这种crash都会有中想死的冲动。虽然说这种crash修改起来超级容易,只是在进行操作前加上类型判断就可以,或者说是直接理解错误了变量的数据类型。
但是这种crash还是经常的出现,为什么呢?我思考了下,我觉得主要主要的原因有两个:
1、使用了“万能”的id;直接上例子代码吧

1
2
3
4
5
id x = @"123";
//many steps...
//...
NSString *str = @"hello";
[x isEqualToString:str];

如上的代码,开始笼统的将变量x定义为id类型的,后面又将x直接当做nsstring类型来做逻辑。也许一个人写的时候,他很清楚这块的逻辑。但是换一个人或者说是过段时间再来修改这块的代码。那么也许就会改成如下代码的可能性。因为这样子既没有报错,也没有warning。

1
2
3
4
5
id x = @(123);
//many steps...
//...
NSString *str = @"hello";
[x isEqualToString:str];

这是一个原因,这个也是我在之前遇到过的。
2、使用自己服务器或者其他渠道的数据。
这点我相信只要做过非纯本地app的人,都深有感触。就是说虽然协议或者说约定好的x这个字段是string类型的。但是保不准到时候会出现错误传个int或者其他类型过来。记得我们cto大人说过,客户端不要轻易相信服务器给过来的数据;反之也是。其实这句话的有一层意思就是说,虽然我们大家都会按照定好的规则来执行。但是保不准有时候会出现问题。但是最终这些所有的问题都是需要由客户端来买单的。所以类型判断必不可少!

然而并不是所有的地方都会记得加上类型判断的。所以还是时不时会产生文字一开始说的那些crash。那么我就在想能不能一劳永逸,在最底层的地方加上类型判断之类的方法解决这个头痛的问题呢。
自然而然的想到了消息传递。其实通常的方法调用

1
[someObject messageName:parameter];

这种方法调用就是消息传递,编译器看到这个会将其转化成如下

1
objc_msgSend(someObject,@selector(messageName:),parameter);

那么先说下向一个实例发送一个消息后,系统的处理是这样的:
1、发送消息([self sendMsg])
2、系统会check是否能够respnse这个消息
3、如果能response则调用想要方法,不能则抛出异常
而在第二步中,系统是如何check实例是否能response消息呢?如果实例没事就有想要的response,那么久会相应返回,如果没有系统就会发出methodSignatureForSelector消息,询问他这个消息是否有效?有效就返回相应的方法地址之类的信息,无效则返回nil。如果nil,Runtime则会发出-doesNotRecognizeSelector:消息,程序这是也就会crash,如果不是nil接着发送forwardInvocation消息。
看了上述的流程。那么是否只要在methodSignatureForSelector这一步,如果在确定本来返回的是nil的时候我自己构造一个将其返回是不是就可以不会闪退了呢。答案是:可以的。methodSignatureForSelector这一步其实也就是对方法的一个签名的返回。 大专栏  类型判断上的小技巧那么如果在这一步按照原有的签名生成的原则伪造一个假的签名,就可以蒙混过关。本会crash的方法将无响应。
以下的是我根据上面的思路写的一个nsobject的扩展类。重写了forwardInvocation和methodSignatureForSelector方法。而这里我只对了几个基础的类型做了检验。因为很多类型是ios非公开的,好像会有点问题。所以相当于先打个补丁在这里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
-(void)forwardInvocation:(NSInvocation *)anInvocation
{
NSLog(@"forwardInvocation");

}

-(NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{

NSMethodSignature *sig = [[self class] instanceMethodSignatureForSelector:selector];

if (!sig) {
BOOL shouldGo = NO;
for (Class class in [self totalClassArr]) {
if ([self isKindOfClass:class] || [self isMemberOfClass:class]) {
shouldGo = YES;
break;
}
}

if (shouldGo) {
//show error

if ([sel rangeOfString:@"set"].location == 0)
{
return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
}
else
{
return [NSMethodSignature signatureWithObjCTypes:"@@:"];
}
}
}

return sig;
}

-(NSArray *)totalClassArr
{
NSMutableArray *arr = [[NSMutableArray alloc]init];
[arr addObject:[NSArray class]];
[arr addObject:[NSNumber class]];
[arr addObject:[NSString class]];
[arr addObject:[NSDictionary class]];
[arr addObject:[NSSet class]];


return [arr copy];
}

下图是有关于消息传递的一张流程图
我觉得能够比较好的理解消息传递这一块

而这篇博文关于消息传递我觉得还是比较可以推荐的 https://www.zybuluo.com/MicroCai/note/64270

原文地址:https://www.cnblogs.com/sanxiandoupi/p/11692578.html