问题引入,我想给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. 如何把excel 数据做dataprovide

    1.  新建一个类,实现接口Iterator import java.io.FileInputStream; import java.io.FileNotFoundException; import ...

  2. (转)assert 断言式编程

    编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设,可以将断言看作是异常处理的一种高级形式.断言表示为一些布尔表达式,程序员相信在程序中的某个特定点该表达式值为真.可以在任何时候启用 ...

  3. 使用scp命令下载服务器文件

    scp -P 7022 admin@ip:/newnow/mysql/backup.sql 7birds.sql -P 指定端口号,默认为22

  4. [sqoop1.99.6] 基于1.99.6版本的一个小例子

    1.创建mysql数据库.表.以及测试数据mysql> desc test;+-------+-------------+------+-----+---------+------------- ...

  5. Leetcode--Swap Nodes in Pairs

    最傻的方法: ListNode *swapPairs(ListNode *head) { if (head == NULL) return NULL; ListNode *temp = ); List ...

  6. int unsigned实验

    create table t1(a int unsigned,b int unsigned); insert into t1 select 1,2; select 1-2 from t1; Error ...

  7. Golang的Interface是个什么鬼

    问题概述 Golang的interface,和别的语言是不同的.它不需要显式的implements,只要某个struct实现了interface里的所有函数,编译器会自动认为它实现了这个interfa ...

  8. Spring Framework的核心:IOC容器的实现

    2.1   Spring IoC容器概述 2.1.1 IoC容器和依赖反转模式 依赖反转的要义,如果合作对象的引用或依赖关系的管理由具体对象来完成,会导致代码的高度耦合和可测性的降低.依赖控制反转的实 ...

  9. Format函数

    Format是一个很常用,却又似乎很烦的方法,本人试图对这个方法的帮助进行一些翻译,让它有一个完整的概貌,以供大家查询之用: 首先看它的声明: function Format(const Format ...

  10. 用C#实现的内存映射

    当文件过大时,无法一次性载入内存时,就需要分次,分段的载入文件 主要是用了以下的WinAPI LPVOID MapViewOfFile(HANDLE hFileMappingObject, DWORD ...