NSString、NSNumber、NSArray、NSDictionary的继承问题

问题引入,我想给NSString类扩展一些新的方法。在Objective-C中可以有两种方法,一是继承,二是类别。
本文先不讨论类别,我们用继承的方法试一下:

  1. @interface StringEx : NSString  
  2. - (void)myFunc;  
  3. @end  
  4.   
  5. @implementation StringEx  
  6. - (void)myFunc {  
  7.     NSLog(@"myFunc");  
  8. }  

我想用下面的方法使用:

  1. StringEx* str = [[StringEx alloc] initWithFormat:@"%d",123];  
  2. [str myFunc];  
  3. [str release];  

编译,没问题。
运行,发生crash,错误代码如下:

  1. *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** initialization method -initWithFormat:locale:arguments: cannot be sent to an abstract object of class StringEx: Create a concrete instance!'  
  2. *** First throw call stack:  
  3. (0x2975012 0x10f7e7e 0x2974deb 0xbc1766 0xbcdc26 0xb2b5bb 0x328a 0x11d1c7 0x11d232 0x11d4da 0x1348e5 0x1349cb 0x134c76 0x134d71 0x13589b 0x1359b9 0x135a45 0x23b20b 0x8c2dd 0x110b6b0 0x40ecfc0 0x40e133c 0x40eceaf 0x12b2bd 0x73b56 0x7266f 0x72589 0x717e4 0x7161e 0x723d9 0x752d2 0x11f99c 0x6c574 0x6c76f 0x6c905 0xb083ab6 0x75917 0x300a 0x39157 0x39747 0x3a94b 0x4bcb5 0x4cbeb 0x3e698 0x1da3df9 0x1da3ad0 0x28eabf5 0x28ea962 0x291bbb6 0x291af44 0x291ae1b 0x3a17a 0x3bffc 0x2cc2 0x27b5)  
  4. libc++abi.dylib: terminate called throwing an exception  

StringEx类是从NSString继承来的,为什么不能使用NSString类的initWithFormat方法呢?

========================================================================
在ios的sdk头文件NSString.h里,init相关方法的上面有这样一句注释
/*** Creation methods ***/
/* In general creation methods in NSString do not apply to subclassers, as subclassers are assumed to provide their own init methods which create the string in the way the subclass wishes. Designated initializers of NSString are thus init and initWithCoder:.
*/
在网上查询了一些资料,大致原因如下:
NSString这个类在设计的时候采用了“抽象工厂”模式,内部是个class cluster,一个类簇。也就是说NSString是个“工厂类”,然后它在外层提供了很多方法接口,但是这些方法的实现是由具体的内部类来实现的。当使用NSString生成一个对象时,初始化方法会判断哪个“自己内部的类”最适合生成这个对象,然后这个“工厂”就会生成这个具体的类对象返回给你。这种又外层类提供统一抽象的接口,然后具体实现让隐藏的,具体的内部类来实现。

这里还有个奇怪的现象:
跟踪[StringEx alloc]返回的对象类型为StringEx类,
跟踪[NSString alloc]返回的对象类型为NSPlaceholderString,而不是NSString类。。。奇怪

我们可以做如下假设:
NSString alloc时有个中间层,就是我们上面看到的NSPlaceholderString,alloc的对象先统一为这个类对象之后,在后面调用 NSPlaceholderString的类方法时,比如initWithFormat:才返回具体的类,即在NSPlaceholderString这一层做个“代理工厂”,根据调用的不同init方法再返回具体的类,比如 NSCFString。
那么为什么我们自己的类调用alloc时,就不返回NSPlaceholderString这个类对象了呢?关键就在于NSString alloc方法的实现。NSString的alloc方法实现可以猜测一下:

  1. @class NSPlaceholderString;  
  2. @interface NSString:(NSObject)  
  3. + (id) alloc;  
  4. @ end  
  5.    
  6. @implementation NSString  
  7. +(id) alloc {  
  8.     if ([self isEquals:[NSString class]]) {  
  9.         return [NSPlaceholderString alloc];  
  10.     }  
  11.     else  
  12.         return [super alloc];  
  13. }  
  14. @end  
  15.    
  16. @interface NSPlaceholderString:(NSString)  
  17. @end  

关键就在于alloc的实现,可以发现,当只用NSString调用alloc的时候,由于self == [NSString class],所以这时返回的是NSPlaceholderString的类对象;而使用其他类(比如派生类)调用alloc时,返回的是super的 alloc,这里也就是[NSObject alloc],而NSObject的alloc方法返回的是调用类的类对象,所以在我们用我们自己的StringEx就是StringEx类的类对象了。

结论:
只扩展类方法的时候,Category已经足够好用了,而上面也解释了Class Cluster和NSString alloc的“怪异”实现。可见继承一个“class cluster”类型的类是多么不容易,如果不熟悉,可能处处是陷阱。所以在有的书上就提出这样的建议:最好不要继承NSString这样的“类簇”类, 同样的还有NSArray,NSDictionary,NSNumber等等。在apple的文档中也提到,建议使用“组合”或者“catogery”来 实现这种扩展,如果你没有非要继承这种“类簇”类的理由的话。

参考:http://www.j2megame.org/index.php/content/view/2622/165.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值