Ios 如何实现apple集群模式中的行为(NSString和NSCFString)

Ios 如何实现apple集群模式中的行为(NSString和NSCFString),ios,objective-c,nsstring,Ios,Objective C,Nsstring,我只是为了测试目的编写以下代码: 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 __NSCFS

我只是为了测试目的编写以下代码:

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
对象。
我的问题是,如果
NSCFString
NSString
的派生类,那么为什么我不能在
NSCFString
上调用
initWithFormat
方法。如果可以停止可见性,如何在代码中实现。

NSCFString
NSCFConstantString
接口是私有的,您不应该使用它们。在查看代码时,很难理解当public
NSString
超类以更简单的方式完成所有需要的工作时,为什么需要深入研究私有子类:

NSString *aStr = @"Foo";
aStr = @"Bar";
或者,如果需要使用以下格式:

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

NSCFString
NSCFConstantString
接口是私有的,您不应该使用它们。在查看代码时,很难理解当public
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"];

问题是您无法重新初始化
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 numberWithBool];该方法返回特定于bool的NSNumber子类的实例,而[NSNumber numberwhithint];返回特定于nit的NSNumber子类的实例。它们都具有相同的接口,NSNumber接口。

一个类集群通常由一个公共类和许多派生自公共类的私有子类实现,因此它们具有相同的接口。查一下号码。通过涉及[NSNumber numberWithBool];该方法返回特定于bool的NSNumber子类的实例,而[NSNumber numberwhithint];返回特定于nit的NSNumber子类的实例。它们都具有相同的接口,即NSNumber接口。

让我们研究一下
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
的具体(私有)子类。在本例中,它返回
\uu NSCFConstantString

如果您将第一行更改为

NSString *factory = [NSMutableString alloc];
输出将更改为:

NSPlaceholderMutableString,实例类:\uu NSCFString

因此,可变字符串和不可变字符串有不同的工厂类,这些工厂返回不同的子类

您甚至可以检查iOS运行时标题中私有子类的层次结构:和


现在,让我们看看在我们刚刚创建的
\unscfconstantstring
实例上调用
initWithString:
时会发生什么

[theInstance initWithString:@"Crash"];
正如你所料,它崩溃了。在stacktrace中,我们可以看到调用了
-[NSString initWithCharactersNoCopy:length:freeWhenDone:][/code>方法,引发了一个异常:

“NSInvalidArgumentException”,原因:“***初始化方法” -initWithCharactersCopy:length:freeWhenDone:无法发送到类uu 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

让我们研究一下
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
的具体(私有)子类。在本例中,它返回
\uu NSCFConstantString

如果您将第一行更改为

NSString *factory = [NSMutableString alloc];
输出将更改为:

NSPlaceholderMutableString,实例类:\uu NSCFString

因此,可变字符串和不可变字符串有不同的工厂类,这些工厂返回不同的子类

你甚至可以检查公关的层次结构