我怎样才能通过苹果(NSString和NSCFString)集群模式实现行为?
我只是简单地写下面的代码来进行testing:
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
通过谷歌我来知道, initWithFormat
将返回NSCFString
对象。 我的问题是,如果NSCFString
是NSString
派生类,那么为什么我不能调用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
所以可变和不可变string有不同的工厂类,这些工厂返回不同的子类。
你甚至可以检查iOS运行时头文件中的私有子类的层次结构: 这里和这里 。
现在让我们看看在我们刚刚创build的__NSCFConstantString
一个实例上调用initWithString:
时会发生什么。
[theInstance initWithString:@"Crash"];
如你所料 – 它崩溃。 在stacktrace中,我们可以看到-[NSString initWithCharactersNoCopy:length:freeWhenDone:]
方法被调用,抛出一个exception:
'NSInvalidArgumentException',原因:'***初始化方法-initWithCharactersNoCopy:length:freeWhenDone:不能被发送到类的抽象对象__NSCFConstantString:创build一个具体的实例!
所以我们可以猜测,这个NSString
类中的初始化实际上是一个抽象方法(Objective-C中没有抽象方法,所以在调用的时候抛出exception)。
该方法在工厂类NSPlaceholderString
。 但是它并没有在所有具体的子类中实现,所以如果你调用任何init...
方法,它会调用抛出exception的NSString
实现。
我们把它放在一起,构buildNSString
类集群的一小部分。 这真的很简单,可能完全不同于真正的实现,但我只是想显示这个想法。
@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
NSCFString
和NSCFConstantString
接口是私有的,你不应该使用它们。 在查看你的代码时,很难判断为什么当公共NSString
超类以更简单的方式完成你需要的一切时,你需要深入私有子类:
NSString *aStr = @"Foo"; aStr = @"Bar";
或者,如果您需要使用格式:
NSString *aStr = [NSString stringWithFormat:@"Foo"];
问题是你不能重新初始化一个NSString
因为它是一个不可变的类,如果你想在创build后改变一个NSString
,你必须使用NSMutableString
。
但是,在你的情况下,你也可以使用一个NSString
,就像这样:
NSString *aStr = [[NSString alloc] initWithFormat:@"Foo"]; aStr = [[NSString alloc] initWithFormat:@"Bar"];
但是,更好的是:
NSString *aStr = @"Foo"; aStr = @"Bar";
如果你想要做的是追加string,你会这样做:
NSMutableString *aStr = [[NSMutableString alloc] initWithString:@"Foo"]; [aStr appendString:@"Bar"];
类集群通常实现一个公共类和许多从公共类派生的私有子类,因此它们具有相同的接口。 检查NSNumber。 通过涉及[NSNumber numberWithBool]; 该方法返回一个NSNumber特定于bool的子类的实例,而[NSNumber numberWithInt]; 返回一个特定于nit的NSNumber子类的实例。 他们两个chard相同的接口,NSNumber接口。
init方法不必返回相同的对象。 要实现相同的行为,只需编写
- (instancetype)initWithSomeArguments { if ((self = [super initWithSomeArguments) != nil) { self = [[RelatedClass alloc] initWithSomeArguments]; } return self; }