问题引入,我想给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. 数据挖掘算法(四)Apriori算法

    参考文献: 关联分析之Apriori算法

  2. [C语言入门笔记]变量与数据类型

    变量与数据类型 什么是变量? 变量是一个变化的量 是内存中的一个空间 变量的定义方法是什么? 数据类型 变量名 = 值; 数据类型有哪些? 整型int 浮点型float double 字符型char ...

  3. flask+html selected 根据后台数据设定默认值

    先给代码 <script> $("#selector_political_status").val('{{ archive.political_status }}'); ...

  4. 面试中常用的__proto__,prototype和原型链,你都了解了吗?

    上一篇随笔主要讲了变量提升的问题,今天我们来讲讲很多前端er在初期很长一段时间内都没有完全搞明白的原型链和构造函数. 1,什么是构造函数 那么要讲到构造函数,必须要有一个函数,所以我们建立一个函数 f ...

  5. SQLite3中自增主键归零方法

    当SQLite数据库中包含自增列时,会自动建立一个名为 sqlite_sequence 的表.这个表包含两个列:name和seq.name记录自增列所在的表,seq记录当前序号(下一条记录的编号就是当 ...

  6. 关于ViewPager、ViewFilpper、ViewFlow三种实现水平向滑动方式的比较

    ViewPagerViewPager类提供了多界面切换的新效果.新效果有如下特征:[1] 当前显示一组界面中的其中一个界面.[2] 当用户通过左右滑动界面时,当前的屏幕显示当前界面和下一个界面的一部分 ...

  7. Ubuntu搭建svn服务器

    一,安装必须的软件包. sudo apt-getinstall subversion 二,基本的SVN服务器配置        1,新建一个目录用于存储SVN所有文件                # ...

  8. 在windows不能正常使用boost og

    现象: 1. 在两个不同的dll中使用static的boost.log.在一个dll中的设置在另一个dll中没有起作用 原因:core::get()返回的是一个单例.在不同的dll中是不同的对象 解决 ...

  9. 《精通C#》索引器与重载操作符(11.1-11.2)

    1.索引器方法结构大致为<modifier><return type> this [argument list],它可以在接口中定义: 在为接口声明索引器的时候,记住声明只是表 ...

  10. ajax 异步插入图片到数据库(多图上传)

    额 大概就这么个样子...截个图 点浏览  选择几张图片 选择完了 确定一下 然后插入数据库 同时在页面中显示插入的图片,代码 也没啥.看下 index.php <html><hea ...