为什么在Objective-C中单独调用alloc和init?

时间:2009-09-06 11:11:50

标签: objective-c cocoa oop init alloc

注意:我对Objective-C比较新,我来自Java和PHP。

有人可以向我解释为什么我总是要先分配然后初始化一个实例吗?

不能在这样的init方法中完成:

+ (MyClass*)init {
    MyClass *instance = [MyClass alloc];
    [instance setFoo:@"bla"];

    return instance;
}

+ (MyClass*)initWithString:(NSString*)text {
    MyClass *instance = [MyClass init];
    [instance setFoo:text];

    return instance;
}
...

这只是旧C日的残骸,还是有些东西我没看到?

我知道这不是问题,因为我总是可以调用alloc和init,但由于它有点乏味,我想至少知道我为什么要这样做。

到目前为止,我仍然喜欢这种语言的表达能力,但这是我想要完全理解的,以便考虑Objective-C的方式。

谢谢!

7 个答案:

答案 0 :(得分:21)

+ new最终向该类发送+ alloc消息,并向+ alloc返回任何返回的消息。

NeXT偏离Stepstone使用+ new消息(这是一个Smalltalk想法)的惯例的原因是,在早期,他们遇到了他们希望能够多次初始化同一个对象的情况。

答案 1 :(得分:16)

因为创建实例并初始化实例是两个独立的作业。

您向该类发送alloc消息以获取未初始化的实例。然后,您必须初始化实例,并且通常有几种方法可以执行此操作。例如:

myStr = [[NSString alloc] init]; //Empty string
myStr = [[NSString alloc] initWithFormat:@"%@.%@", parentKeyPath, key];
myStr = [[NSString alloc] initWithData:utf16data encoding:NSUnicodeStringEncoding error:&error];
myStr = [[NSString alloc] initWithContentsOfURL:URL encoding:NSUTF8StringEncoding error:&error];

每个都以完全不同的方式初始化字符串。初始化字符串的方式取决于您要从中初始化字符串。

当然,没有人喜欢每次都写alloc然后init然后autorelease,所以你通常会有方便的方法(例如stringWithFormat:)给你的步骤。

修改:有关此主题的更多信息,包括评论者提供的基本见解,请参阅我的博文“Reunification”。

答案 2 :(得分:12)

请参阅NSZone

+alloc+allocWithZone:的快捷方式,这是Cocoa为优化内存分配提供的一种机制。

所以你可以选择这样做:

foo = [[NSString allocWithZone:MyZone] initWithString:@"Foo"];
foo2 = [foo copyWithZone:MyZone];

内存区域背后的想法是,如果你有大量经常分配和解除分配的类似对象,那么为这些对象使用单独的内存区域可能会更有效。

为了使分区有效,您需要为每个NSObject子类提供+allocWithZone:,因此您需要分配分配和初始化。您可以创建和使用所需的所有快捷方式,例如+new,但在其下方,您只需要-init方法来初始化已分配的对象。

答案 3 :(得分:7)

“分离实例创建的分配和初始化阶段提供了许多好处。可以使用+ alloc类方法的任何变体来分配实例,然后将任何可用的初始化程序与新实例一起使用。这使得它成为可能。可以创建自己的初始化方法,而无需提供所有分配方法的替代实现。 由于现有方法几乎满足了所有需求,因此很少创建新的分配方法。但是,几乎每个类都会创建一个或多个新的初始值设定项。由于分配和初始化阶段的分离,初始化器实现只需要处理新实例的变量,并且可以完全忽略围绕分配的问题。分离简化了编写初始化器的过程。此外,像-initWithCoder这样的Cocoa标准初始化器:无论实例的内存分配方式如何,都可以使用实例。 分配和初始化分离的一个负面结果是需要了解诸如指定的初始化器之类的约定。您必须知道哪些方法被指定为初始化器以及如何在子类中创建和记录新的初始化器。从长远来看,使用指定的初始化程序简化了软件开发,但有一种观点认为,两阶段创建模式为Cocoa开发人员增加了早期学习曲线。“


(c)Erik M. Buck和Donald A. Yacktman的可可设计模式

答案 4 :(得分:6)

你不必。您可以使用[MyClass new]。这类似于您假设的init方法。

基本上,最初没有垃圾收集的Objective-C将内存分配和类初始化的概念分开。这就是为什么有两种截然不同的方法。当你致电alloc时,你正在明确地分配内存。

答案 5 :(得分:1)

大多数课程都有你要求的。之前你已经得到了答案,为什么会这样,为什么你不总是想要一直使用它,但是如果你阅读文档到类,你会看到很多类方法以这种方式运行,并且经常使用它们。

对于NSString,例如:

+ (id)string  // (Empty string)
+ (id)stringWithFormat:...  // Formatted string (like you use)
+ (id)stringWithContentsOfURL:... // String populated with contents of URL

等等。然后你会使用它:NSString *myString = [NSString stringWithFormat:@"Hello %@\n", userName];

大多数其他类都有这个,比如NSArray:

+ (id)array
+ (id)arrayWithContentsOfFile:...
+ (id)arrayWithContentsOfURL:...
+ (id)arrayWithObjects:...

您只需阅读文档即可。 :)并阅读其他回复,了解为什么你不想太多使用它。

答案 6 :(得分:0)

alloc :将内存分配/赋予对象引用。现在引用拥有内存,但还没有做任何事情。此内存为空(最稀有的情况)或一些匿名数据。

alloc和init :清理/清空已分配的内存。内存由零位启动。

alloc和initwithdata ... :启动已分配的内存,其中所需的数据符合该类的属性。

例如,当您购买地块时,您将获得拥有权。这个情节照原样给你,破砖或老房子可能在那里。这是 alloc

当您清理地块并清除所有污垢和垃圾时。这是使用init进行分配。

当你把它建成一个有价值的房子时,它对你来说变得更有意义。它是 alloc initwith ...