Category是我们在平常写代码时,必然会使用到的一种技术,它有诸多好处:
- 将一个很大的类拆分成相对独立的功能类
- 替换原来的函数
....
例如:
@interface XPMainViewController (AutoLogin)
- (void)autoLogin;
@end
@interface XPMainViewController (SyncConfig)
- (void)syncRemoteConfig;
@end
看起来Category真的非常不错,可以帮我们模块化
清晰化我们的代码,但是它真的靠谱吗?我们来验证一下。
首先,我们先创建一个NSURLSessionTask的Category:
@interface NSURLSessionTask (Test)
- (NSString*)testName;
@end
@implementation NSURLSessionTask (Test)
- (NSString*)testName {
return @"test";
}
@end
Category很简单,就是给NSURLSessionTask增加了一个叫testName的函数!
然后我们来调用一下试试:
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"http://www.baidu.com"]];
NSLog(@"%@",[task testName]);
代码写到这,我们满怀信心的Commnd+r,但是令人意想不到的事情发生了,我们的程序crash了,堆栈显示为:
**2016-03-06 13:11:13.806 URLSessionTest[2704:94286] -[__NSCFLocalDataTask testName]: unrecognized selector sent to instance 0x7fa25a5f8e10**
呵呵,看堆栈的信息提示,这个crash引起的原因是:没有testName函数啊!!!!
Oh,my god!什么玩意,我不是已经使用Category技术为NSURLSessionTask增加了这个函数了吗?怎么还是没有提示无实现呢?
等等,我们再仔细看看。什么!!!__NSCFLocalDataTask无testName实现!这是什么玩意,我明明代码写的是NSURLSessionTask啊!怎么回事???WTF
咦,难道NSURLSession返回的不是NSURLSessionTask???不得已,log下class、supperclass等等看看。
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"http://www.baidu.com"]];
NSLog(@"%@", [task class]);
NSLog(@"%@", [task superclass]);
NSLog(@"%@", [[task superclass] superclass]);
NSLog(@"%@", [[[task superclass] superclass] superclass]);
得到结果:
**2016-03-06 13:11:13.805 URLSessionTest[2704:94286] __NSCFLocalDataTask**
**2016-03-06 13:11:13.806 URLSessionTest[2704:94286] __NSCFLocalSessionTask**
**2016-03-06 13:11:13.806 URLSessionTest[2704:94286] __NSCFURLSessionTask**
**2016-03-06 13:11:13.806 URLSessionTest[2704:94286] NSURLSessionTask**
在iOS9下,得到这些结果,并且iOS7下还不是这个结果,WTF!!!
但是好歹原因总算是找到了:NSURLSession的dataTaskWithURL返回的是一个private class,压根就不是NSURLSessionTask,它的superClass的superClass的superClass才是NSURLSessionTask!好长的继承链...
一次意外的debug,也获得了一个意外的收货。
其实Category这个技术和这次的crash没有直接关系,。可以说这是Apple留的坑吧。