簡體   English   中英

我如何通過Apple(NSString和NSCFString)實現群集模式中的行為

[英]How can i implement the behaviour as in cluster pattern by apple (NSString and NSCFString)

我只是在編寫以下代碼以進行測試:

NSString *aStr = [[NSString alloc] initWithFormat:@"Foo"];
aStr = [aStr initWithFormat:@"Bar"];//Crashed here

我收到以下錯誤:

*** initialization method -initWithFormat:locale:arguments: cannot be sent to an abstract object of class __NSCFString: Create a concrete instance!

如果我寫下面的代碼,同樣的事情會發生

NSString *aStr = [NSString alloc];
aStr = [aStr initWithFormat:@"Foo"];
aStr = [aStr initWithFormat:@"Bar"]; //Crashed here

通過Google,我知道initWithFormat將返回NSCFString對象。 我的問題是,如果NSCFStringNSString派生類,那么為什么我不能在NSCFString上調用initWithFormat方法。 如果可以停止可見性,該如何在代碼中實現。

讓我們研究一下NSString類集群在內部如何工作:

NSString *factory = [NSString alloc];
NSString *theInstance = [factory initWithString:@"I am constant"];
NSLog(@"factory class: %@, instance class: %@", [factory class], [theInstance class]);

輸出為:

factory class: NSPlaceholderString, instance class: __NSCFConstantString

如您所見, alloc方法返回NSPlaceholderString的實例。 這是一個“工廠”類,它實現在NSString聲明的所有init...方法。 這些方法返回NSString具體(私有)子類。 在此示例中,它返回__NSCFConstantString

如果將第一行更改為

NSString *factory = [NSMutableString alloc];

輸出將更改為:

NSPlaceholderMutableString,實例類:__NSCFString

因此,可變和不可變字符串具有不同的工廠類,並且這些工廠返回不同的子類。

您甚至可以在iOS運行時標頭( 此處此處)中檢查私有子類的層次結構。


現在,讓我們看看在剛剛創建的__NSCFConstantString實例上調用initWithString:時會發生什么。

[theInstance initWithString:@"Crash"];

如您所料-它崩潰了。 -[NSString initWithCharactersNoCopy:length:freeWhenDone:] ,我們可以看到-[NSString initWithCharactersNoCopy:length:freeWhenDone:]方法被調用,並引發異常:

“ NSInvalidArgumentException”,原因:“ ***初始化方法-initWithCharactersNoCopy:length:freeWhenDone:無法發送給類__NSCFConstantString的抽象對象:創建一個具體實例!”

因此我們可以猜測, NSString類中的該初始化器實際上是一個抽象方法(種類-Objective-C中沒有抽象方法,因此在調用時會引發異常)。

此方法在工廠類NSPlaceholderString 但是它並不是在所有具體的子類中都實現,因此,如果調用任何init...方法,它將調用NSString實現,該實現將引發異常。


讓我們將它們放在一起,並構建NSString類集群的一小部分。 它確實簡化了,可能與實際實現完全不同,但我只是想展示一下這個想法。

@interface NSPlaceholderString : NSString
@end

@interface __NSCFConstantString : NSString
@end


@implementation NSString

+ (instancetype)alloc {
    return [[NSPlaceholderString alloc] init];
}

- (instancetype)initWithCharactersNoCopy:(unichar *)characters length:(NSUInteger)length freeWhenDone:(BOOL)freeBuffer {
    [NSException raise:NSInvalidArgumentException format:@" initialization method -initWithCharactersNoCopy:length:freeWhenDone: cannot be sent to an abstract object of class %@: Create a concrete instance!'", [self class]];
    return nil;
}

- (instancetype)initWithString:(NSString *)aString {
//this method has to call the "abstract" initializer somewhere. The real implementation is probably more complex, this single line is here for simplicity
    return [self initWithCharactersNoCopy:[aString UTF8String] length:[aString length] freeWhenDone:YES];       
}

@end

@implementation NSPlaceholderString

- (instancetype)initWithCharactersNoCopy:(unichar *)characters length:(NSUInteger)length freeWhenDone:(BOOL)freeBuffer {
    __NSCFConstantString *concreteClassInstance = ...; // create the concrete instance. 
    return concreteClassInstance;
}

@end

@implementation __NSCFConstantString

//implement all the needed methods here. But do NOT implement initWithCharactersNoCopy:length:freeWhenDone:

@end

NSCFStringNSCFConstantString接口是私有的,您不應使用它們。 在查看代碼時,很難分辨為什么當公共NSString超類以更簡單的方式完成所需的一切時,為什么需要深入研究私有子類:

NSString *aStr = @"Foo";
aStr = @"Bar";

或者,如果您需要使用以下格式:

NSString *aStr = [NSString stringWithFormat:@"Foo"];

問題是您不能重新初始化NSString因為它是一個不可更改的類,如果要在創建后更改NSString ,則必須使用NSMutableString

但是,根據您的情況,還可以使用NSString ,如下所示:

NSString *aStr = [[NSString alloc] initWithFormat:@"Foo"];
aStr = [[NSString alloc] initWithFormat:@"Bar"];

但是,最好是:

NSString *aStr = @"Foo";
aStr = @"Bar";

如果您要執行的操作是附加字符串,則可以執行以下操作:

NSMutableString *aStr = [[NSMutableString alloc] initWithString:@"Foo"];
[aStr appendString:@"Bar"];

一個類集群通常用一個公共類和從公共類派生的許多私有子類來實現,因此它們具有相同的接口。 檢查NSNumber。 通過涉及[NSNumber numberWithBool]; 該方法返回特定於bool的while [NSNumber numberWithInt]的NSNumber子類的實例; 返回特定於nit的NSNumber的子類的實例。 他們兩個都使用同一個接口,即NSNumber接口。

init方法不必返回相同的對象。 要實現相同的行為,只需編寫

- (instancetype)initWithSomeArguments
{
    if ((self = [super initWithSomeArguments) != nil)
    {
        self = [[RelatedClass alloc] initWithSomeArguments];
    }

    return self;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM