问题引入,我想给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. 关于malloc的一些想法

    之前一段时间翻阅过一些内存分配的资料,这次终于能将其整理记录下来了. c标准库里有两个耳熟能详的函数,用于对堆空间的内存进行分配和释放,它们分别是: malloc.负责分配一个指定大小的一块内存给调用 ...

  2. physx之刚体运动

    [线性速度与角速度] PxRigidBody的速度由线性速度和角速度构成, 可以分别通过setLinearVelocity和setAngularVelocity进行设置.需要注意的是, 刚体的角速度总 ...

  3. c# 判断当前时间是否在 工作日时间段内

    #region //获取当前周几 private string _strWorkingDayAM = "08:30";//工作时间上午08:00 private string _s ...

  4. Fragment:关于Avoid non-default constructors in fragments的错误

    在android开发中,写了一个关于继承Fragment的类时,如果有重载构造函数时,会提示“Avoid non-default constructors in fragments: use a de ...

  5. Python笔记总结week1

    1. Python介绍: 发明者:Guido 应用范围:网络应用.科学运算.GUI程序.系统管理工具.其他程序 优势:简单易懂.开发效率高.高级语言.可移植性.可扩展性.可嵌入性. 缺点:速度慢,代码 ...

  6. Java中sql语句的引号问题

    1..sql语句 在数据库中,当我们查询语句时,会使用类似的语句: Select * from userinfo where userid='1' or 1; Select * from userin ...

  7. leetcode Pascal's triangle

    #include <stdio.h> int** generate(int numRows, int** columnSizes) { if (numRows == 0) { column ...

  8. MHA+Atlas+mysql一主一从开启gtid安装配置与实验

    各节点架构 (说明:生产环境有两个节点可以组成一套完整集群,我是测试环境,因此对于manager以及atlas和binlog server都是单点,如果生产环境,相应的将manager以及atlas和 ...

  9. IOS开发之画图形

    1 画线 2 画线第二个方法  相对方法1简洁 3 矩形 4 圆 5 弧线 6画文字(略) 7 画图片(略)

  10. javascript 中的借鸡生蛋

    如题所说,其实指的是 js 中的内置函数和 apply 的结合使用.这种用法很讨巧,读过 jQuery 源码的小伙伴都知道,它在里面被应用的非常广泛和精妙! 梨子:找出一个数组中的最大值和最小值 普遍 ...