深入探討NSString籍琳、NSArray和NSDictionary為什么不可以繼承

版本記錄

版本號(hào) 時(shí)間
V1.0 2017.08.31

前言

NSStringNSArrayNSDictionary是大家一定會(huì)用到的類贷祈,但是大家有沒有發(fā)現(xiàn)趋急,除了系統(tǒng)的三個(gè)子類NSMutableArrayNSMutableStringNSMutableDictionary繼承自它們幾個(gè)势誊,你見過自定義的類繼承它們的嗎呜达?下面我們就深入探討這個(gè)問題。

問題提出

就我目前做的這些項(xiàng)目而言粟耻,說實(shí)話我沒見過誰自定義類繼承NSString查近、NSArrayNSDictionary,是因?yàn)橥耆珱]有這個(gè)功能要求上的必要自定義類繼承自它們挤忙,還是因?yàn)樘O果原則上就不允許我們自定義繼承它們的類呢嗦嗡?

下面我們就研究一下。


問題分析

下面我們還是先看代碼饭玲,自定義一個(gè)類繼承自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

運(yùn)行就發(fā)現(xiàn)奔潰了茄厘,輸出信息如下所示:

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:調(diào)用后矮冬,會(huì)調(diào)用NSString initWithCharactersNoCopy:length:freeWhenDone:。然后就接著調(diào)用NSException raise:format:次哈,接著就會(huì)拋出異常胎署。

這里需要理解一下:這里就是類簇的問題,類簇就是把一組有共同特性的子類都繼承說一個(gè)父類窑滞,當(dāng)我們?cè)谛枰鱾€(gè)子類的時(shí)候琼牧,我們就需要去操作父類就可以,運(yùn)用抽象工廠模式哀卫,父類會(huì)根據(jù)你的需求返回給你相應(yīng)的子類巨坊。

下面我們?cè)诳匆幌吕?/p>

#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

下面看輸出結(jié)果

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調(diào)用alloc的時(shí)候會(huì)生成一個(gè)對(duì)象NSPlaceholderString
  • NSString調(diào)用init的時(shí)候會(huì)生成對(duì)象__NSCFConstantString此改,而NSMutableString調(diào)用init的時(shí)候會(huì)生成對(duì)象__NSCFString趾撵。
  • JJString調(diào)用allocinit的時(shí)候還是JJString對(duì)象。

為什么會(huì)是這個(gè)樣子共啃,其實(shí)可以從下面幾個(gè)情況著手:

  • 這里占调,NSPlaceholderString是一個(gè)中間對(duì)象。后面的- init- initWithXXXXX消息都是發(fā)送給這個(gè)中間對(duì)象移剪,再由它做工廠究珊,生成真的對(duì)象分別是這里的NSCFConstantStringNSCFString類。

那么為什么我們自己的類調(diào)用alloc時(shí)纵苛,就不返回NSPlaceholderString這個(gè)類對(duì)象了呢剿涮?關(guān)鍵就在于NSString alloc方法的實(shí)現(xiàn)。NSString的alloc方法實(shí)現(xiàn)可以猜測(cè)一下:

@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 

關(guān)鍵就在于alloc的實(shí)現(xiàn)赶站,可以發(fā)現(xiàn)幔虏,當(dāng)只用NSString調(diào)用alloc的時(shí)候,由于self == [NSString class]贝椿,所以這時(shí)返回的是NSPlaceholderString的類對(duì)象想括;而使用其他類(比如派生類)調(diào)用alloc時(shí),返回的是super的 alloc烙博,這里也就是[NSObject alloc]瑟蜈,而NSObject的alloc方法返回的是調(diào)用類的類對(duì)象,所以在我們用我們自己的LBString就是LBString類的類對(duì)象了所以沒有NSString 的一些方法了渣窜。

參考文章

1. Foundation庫下的NSString,NSArray, NSDictionary……可以被繼承嗎

后記

感謝這個(gè)技術(shù)大牛給的技術(shù)指導(dǎo)铺根,致敬,引用參考已經(jīng)列到參考文章里面了乔宿。謝謝大家位迂,希望對(duì)大家有所幫助。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市掂林,隨后出現(xiàn)的幾起案子臣缀,更是在濱河造成了極大的恐慌,老刑警劉巖泻帮,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件精置,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡锣杂,警方通過查閱死者的電腦和手機(jī)脂倦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來元莫,“玉大人赖阻,你說我怎么就攤上這事∑饩海” “怎么了政供?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)朽基。 經(jīng)常有香客問我布隔,道長(zhǎng),這世上最難降的妖魔是什么稼虎? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任衅檀,我火速辦了婚禮,結(jié)果婚禮上霎俩,老公的妹妹穿的比我還像新娘哀军。我一直安慰自己,他們只是感情好打却,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布杉适。 她就那樣靜靜地躺著,像睡著了一般柳击。 火紅的嫁衣襯著肌膚如雪猿推。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天捌肴,我揣著相機(jī)與錄音蹬叭,去河邊找鬼。 笑死状知,一個(gè)胖子當(dāng)著我的面吹牛秽五,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播饥悴,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼坦喘,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼盲再!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起瓣铣,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤洲胖,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后坯沪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡擒滑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年腐晾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丐一。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡藻糖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出库车,到底是詐尸還是另有隱情巨柒,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布柠衍,位于F島的核電站洋满,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏珍坊。R本人自食惡果不足惜牺勾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望阵漏。 院中可真熱鬧驻民,春花似錦、人聲如沸履怯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叹洲。三九已至柠硕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間疹味,已是汗流浹背仅叫。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留糙捺,地道東北人诫咱。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像洪灯,于是被迫代替她去往敵國(guó)和親坎缭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子竟痰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容