15

注意:我对 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 的方式思考。

谢谢!

4

7 回答 7

21

+new 最终向类发送一个 +alloc 消息,并向从 +alloc 返回的任何内容发送一个 -init 消息。

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

于 2009-09-06T11:23:57.257 回答
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,所以你通常有方便的方法(例如, )为你完成所有三个步骤。autoreleasestringWithFormat:

编辑:有关此主题的更多信息,包括评论者的基本见解,请参阅我的博客文章“<a href="http://boredzo.org/blog/archives/2010-02-02/alloc-init-vs-new" rel="nofollow noreferrer">统一”。

于 2009-09-06T11:18:35.520 回答
12

NSZone

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

因此,您可以选择执行以下操作:

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

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

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

于 2009-09-06T16:17:05.037 回答
7

像 -initWithCoder 这样的 Cocoa 标准初始化程序:无论实例的内存分配方式如何,都可以使用实例。分配和初始化分离的一个负面后果是需要了解诸如指定初始化程序之类的约定。您必须知道哪些方法是指定初始化程序以及如何在子类中创建和记录新的初始化程序。从长远来看,使用指定的初始化程序可以简化软件开发,但是有一个论点是两阶段创建模式增加了 Cocoa 开发人员的早期学习曲线。” 分配和初始化分离的一个负面后果是需要了解诸如指定初始化程序之类的约定。您必须知道哪些方法是指定初始化程序以及如何在子类中创建和记录新的初始化程序。从长远来看,使用指定的初始化程序可以简化软件开发,但是有一个论点是两阶段创建模式增加了 Cocoa 开发人员的早期学习曲线。” 分配和初始化分离的一个负面后果是需要了解诸如指定初始化程序之类的约定。您必须知道哪些方法是指定初始化程序以及如何在子类中创建和记录新的初始化程序。从长远来看,使用指定的初始化程序可以简化软件开发,但是有一个论点是两阶段创建模式增加了 Cocoa 开发人员的早期学习曲线。”


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

于 2010-10-16T14:39:02.923 回答
6

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

基本上,最初没有垃圾收集的Objective-C将内存分配和类初始化的概念分开。这就是为什么有两种不同的方法。当您调用 时alloc,您正在显式分配内存。

于 2009-09-06T11:13:22.177 回答
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:...

你只需要阅读文档。:) 并阅读其他关于您为什么不想过多使用它的回复。

于 2009-09-06T12:09:02.970 回答
0

alloc:内存被分配/给予对象引用。现在reference拥有了内存,但还没有做任何事情。此内存为空(极少数情况)或带有一些匿名数据。

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

alloc 和 initwithdata...:分配的内存使用符合类属性的所需数据启动。

例如,当您购买地块时,您将获得财产。这个地块是原样给你的,破砖或老房子可能在那里。这是分配

当您清洁您的地块并清除所有污垢和垃圾时。这是用 init 分配的。

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

于 2015-12-23T09:18:49.880 回答