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

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

我想用下面的方法使用:

  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. @implementation NSString
  6. +(id) alloc {
  7. if ([self isEquals:[NSString class]]) {
  8. return [NSPlaceholderString alloc];
  9. }
  10. else
  11. return [super alloc];
  12. }
  13. @end
  14. @interface NSPlaceholderString:(NSString)
  15. @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

NSString,NSArray,NSNumber等类的继承问题的更多相关文章

  1. NSValue NSNumber NSData类

    NSValue NSNumber NSData类 步骤1 NSValue 我们先看看NSValue能做什么: 一个NSValue对象是用来存储一个C或者Objective-C数据的简单容器.它可以保存 ...

  2. ios学习笔记(二)之Objective-C类、继承、类别和协议

    二:Objective-C类与继承和协议 在前面已经提过了对象的初始化,这里首先讲的是变量. 2.1 变量 局部变量(内部变量): 局部变量是在方法内作定义说明的,其作用域仅限于方法内,离开方法后使用 ...

  3. Objective-C 类的继承、方法的重写和重载

    一.类的继承 Objective-c中类的继承与C++类似,不同的是Objective-c不支持多重继承,一个类只能有一个父类,单继承使Objective-c的继承关系很简单,易于管理程序.Objec ...

  4. Objective-c 类的继承 方法重写 方法重载

    一.类的继承 Objective-c中类的继承与C++类似,不同的是Objective-c不支持多重继承,一个类只能有一个父类,单继承使Objective-c的继承关系很简单,易于管理程序. Obje ...

  5. UML类图(上):类、继承和实现

    面向对象设计 对于一个程序员来说,在工作的开始阶段通常都是别人把东西设计好,你来做.伴随着个人的成长,这个过程将慢慢变成自己设计一部分功能来实现,自己实现.如果要自己设计,无论是给自己看,还是给别人看 ...

  6. 【Python五篇慢慢弹(5)】类的继承案例解析,python相关知识延伸

    类的继承案例解析,python相关知识延伸 作者:白宁超 2016年10月10日22:36:57 摘要:继<快速上手学python>一文之后,笔者又将python官方文档认真学习下.官方给 ...

  7. (转)Java:类与继承

    原文地址: http://www.cnblogs.com/dolphin0520/p/3803432.html 对于面向对象的程序设计语言来说,类毫无疑问是其最重要的基础.抽象.封装.继承.多态这四大 ...

  8. iBatis.net 类的继承extends和懒加载

    <resultMaps> <resultMap id="FullResultMap" class="t_c_team_member_permission ...

  9. python 类定义 继承

    0 前言 系统:win7 64bit IDE : python(x,y) 2.7.6.1 IDE集成的解释器:Python 2.7.6 (default, Nov 10 2013, 19:24:18) ...

随机推荐

  1. Contiki-一个进程的例子

    进程调度器 进程调度器的作用是调用进程.进程调度器通过调用实现进程线程的函数来调用进程.Contiki中所有的进程被设计为响应传递到进程中的事件,或者相应进程请求的轮询.进程调度器在调度进程的时候会将 ...

  2. VB发送后台按键和组合键

    http://files.cnblogs.com/files/liuzhaoyzz/VB%E5%8F%91%E9%80%81%E5%90%8E%E5%8F%B0%E7%BB%84%E5%90%88%E ...

  3. hdu acm 简单暴力1004

    字符串匹配函数strcmp 直接使用来判断两字符串是否完全相等 用数组存每个单词的个数时  初始化为零就错 初始化为一时就正确  也不知道为什么

  4. excel读取

    一.jar包 二.工具类 package excel; import java.io.FileInputStream; import java.io.FileNotFoundException; im ...

  5. angularjs不同页面间参数的传递

    1.在路由中定义要接收的参数 .state('userDetails', { url: '/userDetails?phone', //以?为标识接收参数 templateUrl: 'assets/v ...

  6. Windows下 VM12虚拟机安装OS X 10.11 和VM TOOLS

    Windows下虚拟机安装Mac OS X —– VMware Workstation12安装Mac OS X 10.11 本文即将介绍WIN虚拟MAC的教程.完整详细教程(包含安装中的一些问题) [ ...

  7. 【NodeJS线程】Boss和他的职员们

    >>>[说明]还是一如既往的,这篇文章是从我的个人博客里挪过来的.原文参见:http://www.jscon.co/coding/frontend/nodejs_fork_child ...

  8. YY前端课程3

    1. 常用的字符实体(html实体):空格=      <=<       >=>       版权符号=© 2. ID就像身份证号一样,是唯一的,html页面的ID不能重复: ...

  9. Web APP开发技巧总结(转)

    一.META/LINK相关: 1.百度禁止转码 通过百度手机打开网页时,百度可能会对你的网页进行转码,往你页面贴上它的广告,非常之恶心.不过我们可以通过这个meta标签来禁止它: <meta h ...

  10. mongodump 备份

    规划 副本集,其中加了个隐藏节点,用来做备份,所以备份脚本直接在隐藏节点做,目前数据不大,直接本机磁盘存储,后续如果数据集大,那么在本地存最近一天的备份,远程根据需求存储几天的备份 创建备份用户 db ...