深入探讨NSString、NSArray和NSDictionary为什么不可以继承

版本记录

版本号 时间
V1.0 2017.08.31

前言

NSStringNSArrayNSDictionary是大家一定会用到的类,但是大家有没有发现,除了系统的三个子类NSMutableArrayNSMutableStringNSMutableDictionary继承自它们几个,你见过自定义的类继承它们的吗?下面我们就深入探讨这个问题。

问题提出

就我目前做的这些项目而言,说实话我没见过谁自定义类继承NSStringNSArrayNSDictionary,是因为完全没有这个功能要求上的必要自定义类继承自它们,还是因为苹果原则上就不允许我们自定义继承它们的类呢?

下面我们就研究一下。


问题分析

下面我们还是先看代码,自定义一个类继承自NSString,如下所示。

1. JJString.h
#import <Foundation/Foundation.h>

@interface JJString : NSString

@end
2. JJTestStringVC.h
#import <UIKit/UIKit.h>

@interface JJTestStringVC : UIViewController

@end
3. JJTestStringVC.m
#import "JJTestStringVC.h"
#import "JJString.h"

@interface JJTestStringVC ()

@end

@implementation JJTestStringVC

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    JJString *str = [JJString stringWithString:@"ABC"];
    NSLog(@"%@", str);
}

@end

运行就发现奔溃了,输出信息如下所示:

2017-08-31 19:06:37.081 JJOC[1374:34198] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** initialization method -initWithCharactersNoCopy:length:freeWhenDone: cannot be sent to an abstract object of class JJString: Create a concrete instance!'
*** First throw call stack:
(
    0   CoreFoundation                      0x000000010d8b4b0b __exceptionPreprocess + 171
    1   libobjc.A.dylib                     0x000000010cf3b141 objc_exception_throw + 48
    2   CoreFoundation                      0x000000010d91d625 +[NSException raise:format:] + 197
    3   Foundation                          0x000000010cb45730 -[NSString initWithCharactersNoCopy:length:freeWhenDone:] + 14
    4   Foundation                          0x000000010ca49869 +[NSString stringWithString:] + 45
    5   JJOC                                0x000000010c75a3fe -[JJTestStringVC viewDidLoad] + 94
    6   UIKit                               0x000000010f30f01a -[UIViewController loadViewIfRequired] + 1235
    7   UIKit                               0x000000010f34de6c -[UINavigationController _layoutViewController:] + 56
    8   UIKit                               0x000000010f34e74a -[UINavigationController _updateScrollViewFromViewController:toViewController:] + 466
    9   UIKit                               0x000000010f34e8bb -[UINavigationController _startTransition:fromViewController:toViewController:] + 127
    10  UIKit                               0x000000010f34fa03 -[UINavigationController _startDeferredTransitionIfNeeded:] + 843
    11  UIKit                               0x000000010f350b41 -[UINavigationController __viewWillLayoutSubviews] + 58
    12  UIKit                               0x000000010f54260c -[UILayoutContainerView layoutSubviews] + 231
    13  UIKit                               0x000000010f22f55b -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1268
    14  QuartzCore                          0x000000010edae904 -[CALayer layoutSublayers] + 146
    15  QuartzCore                          0x000000010eda2526 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 370
    16  QuartzCore                          0x000000010eda23a0 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 24
    17  QuartzCore                          0x000000010ed31e92 _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 294
    18  QuartzCore                          0x000000010ed5e130 _ZN2CA11Transaction6commitEv + 468
    19  QuartzCore                          0x000000010ed5eb37 _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 115
    20  CoreFoundation                      0x000000010d85a717 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
    21  CoreFoundation                      0x000000010d85a687 __CFRunLoopDoObservers + 391
    22  CoreFoundation                      0x000000010d83f038 CFRunLoopRunSpecific + 440
    23  UIKit                               0x000000010f16608f -[UIApplication _run] + 468
    24  UIKit                               0x000000010f16c134 UIApplicationMain + 159
    25  JJOC                                0x000000010c75a37f main + 111
    26  libdyld.dylib                       0x0000000111b3a65d start + 1
    27  ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

从上面我们可以看见:

  • 方法NSString stringWithString:调用后,会调用NSString initWithCharactersNoCopy:length:freeWhenDone:。然后就接着调用NSException raise:format:,接着就会抛出异常。

这里需要理解一下:这里就是类簇的问题,类簇就是把一组有共同特性的子类都继承说一个父类,当我们在需要各个子类的时候,我们就需要去操作父类就可以,运用抽象工厂模式,父类会根据你的需求返回给你相应的子类。

下面我们在看一下例子

#import "JJTestStringVC.h"
#import "JJString.h"

@interface JJTestStringVC ()

@end

@implementation JJTestStringVC

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    id obj1 = [NSString alloc];
    id obj2 = [NSMutableString alloc];
    
    id obj3 = [obj1 init];
    id obj4 = [obj2 init];
    
    id obj5 = [JJString alloc];
    id obj6 = [obj5 init];
    
    NSLog(@"obj1 = %@", [obj1 class]);
    NSLog(@"obj2 = %@", [obj2 class]);
    NSLog(@"obj3 = %@", [obj3 class]);
    NSLog(@"obj4 = %@", [obj4 class]);
    NSLog(@"obj5 = %@", [obj5 class]);
    NSLog(@"obj6 = %@", [obj6 class]);
}

@end

下面看输出结果

2017-08-31 20:34:54.585 JJOC[2358:75128] obj1 = NSPlaceholderString
2017-08-31 20:34:54.585 JJOC[2358:75128] obj2 = NSPlaceholderMutableString
2017-08-31 20:34:54.585 JJOC[2358:75128] obj3 = __NSCFConstantString
2017-08-31 20:34:54.586 JJOC[2358:75128] obj4 = __NSCFString
2017-08-31 20:34:54.586 JJOC[2358:75128] obj5 = JJString
2017-08-31 20:34:54.586 JJOC[2358:75128] obj6 = JJString

这里大家可以看到:

  • NSStringNSMutableString调用alloc的时候会生成一个对象NSPlaceholderString
  • NSString调用init的时候会生成对象__NSCFConstantString,而NSMutableString调用init的时候会生成对象__NSCFString
  • JJString调用allocinit的时候还是JJString对象。

为什么会是这个样子,其实可以从下面几个情况着手:

  • 这里,NSPlaceholderString是一个中间对象。后面的- init- initWithXXXXX消息都是发送给这个中间对象,再由它做工厂,生成真的对象分别是这里的NSCFConstantStringNSCFString类。

那么为什么我们自己的类调用alloc时,就不返回NSPlaceholderString这个类对象了呢?关键就在于NSString alloc方法的实现。NSString的alloc方法实现可以猜测一下:

@class NSPlaceholderString;  

@interface NSString:(NSObject)  

+  (id) alloc;  

@ end  

@implementation NSString  

+(id) alloc 
{  
    if ([self isEquals:[NSString class]]) {  
        return [NSPlaceholderString alloc];  
    }  
    else  
        return [super alloc];  
}  
@end  

@interface NSPlaceholderString:(NSString)  

@end 

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

参考文章

1. Foundation库下的NSString,NSArray, NSDictionary……可以被继承吗

后记

感谢这个技术大牛给的技术指导,致敬,引用参考已经列到参考文章里面了。谢谢大家,希望对大家有所帮助。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容