使用 alloc 和 init

发布于 2024-10-04 01:45:07 字数 2402 浏览 6 评论 0原文

我们知道alloc/init的完整模式,alloc和init必须结合起来。

NSObject *myObj = [[NSObject alloc] init];

1- init 方法从另一个源接收对象(不是从 alloc、new、copy 或类似的或保留的),因此根据基本内存管理规则,它不是所有者,并且不能释放它。然而,“分配和初始化对象/返回的对象”文章说,init可以释放接收者。

当它违反基本规则时,这怎么可能?

2-此外,在同一篇文章中,init 可以返回另一个对象或 nil。所以,在这种情况下,当我们使用alloc/init的完整模式时,我们无法释放alloc返回的对象,而只能释放init返回的对象,并且init代替我们释放从alloc接收的对象。

但是 init 不是 alloc、new、copy 或类似的方法,因此我们必须不释放从它返回的对象,因为它不给我们对象的所有权。

我们如何释放从 init 返回的对象,尽管这违反了基本规则?

3-或者,为了遵守同一篇文章的最后一段,我们必须接受 init 方法作为特殊方法case 并使用 alloc/init 模式作为基本规则的例外?

内存管理基本规则:

  • 您只能释放或自动释放您拥有的对象。

分配和初始化对象/返回的对象:

但是,在某些情况下,这种责任可能意味着返回与接收者不同的对象。例如,如果一个类保留命名对象的列表,它可能提供 initWithName: 方法来初始化新实例。如果每个名称不能有多个对象,则 initWithName: 可能会拒绝将相同的名称分配给两个对象。当要求为新实例分配一个已被另一个对象使用的名称时,它可能会释放新分配的实例并返回另一个对象 - 从而确保名称的唯一性,同时提供所要求的内容,具有所请求名称的实例。

在某些情况下,init...方法可能无法执行其要求执行的操作。例如, initFromFile: 方法可能会从作为参数传递的文件中获取所需的数据。如果它传递的文件名与实际文件不对应,它将无法完成初始化。在这种情况下,init...方法可以释放接收者并返回 nil,表明无法创建所请求的对象。

因为 init...方法可能返回新分配的接收器之外的对象,甚至返回 nil程序使用初始化方法返回的值非常重要,而不是只是由alloc或allocWithZone返回的:。下面的代码非常危险,因为它忽略了init的返回。

id anObject = [SomeClass alloc];
[一个对象初始化];
[anObject someOtherMessage];

相反,要安全地初始化对象,您应该将分配消息和初始化消息合并在一行代码中。

id anObject = [[SomeClass alloc] init];
[anObject someOtherMessage];

http://developer.apple.com/库/ios/#documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocAllocInit.html

We know about the complete pattern of alloc/init that alloc and init must be combined.

NSObject *myObj = [[NSObject alloc] init];

1- init method receives the object from another source(not from a alloc,new,copy or alike or retained) so according to the fundamental memory management rule its not the owner and it must not release it. However, "Allocating and Initializing Objects / The Returned Object" article says that init could free the receiver.

How could this be possible when its against the fundamental rule?

2- Also, from the same article, init could return another object or nil. So, in this case, when we use the complete pattern of alloc/init, we could not release the object returned by alloc but we could only release the object returned from init and, init releases the object it received from alloc instead of us.

But init is not a alloc,new,copy or alike method so we must not release the object returned from it as it does not give us the ownership of object.

How could we release the object returned from init although this is against the fundamental rule?

3- Or, to adhere to the last paragraph of the same article, must we accept init method as a special case and use alloc/init pattern as an exception to the fundamental rule?

Memory Management Fundamental Rule:

  • You only release or autorelease objects you own.
    • You take ownership of an object if you create it using a method whose name begins with “alloc” or “new” or contains “copy” (for example, alloc, newObject, or mutableCopy), or if you send it a retain message.
    • You use release or autorelease to relinquish ownership of an object. autorelease just means “send a release message in the future” (to understand when this will be, see “Autorelease Pools”).
      http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html

Allocating and Initializing Objects / The Returned Object:

However, in some cases, this responsibility can mean returning a different object than the receiver. For example, if a class keeps a list of named objects, it might provide an initWithName: method to initialize new instances. If there can be no more than one object per name, initWithName: might refuse to assign the same name to two objects. When asked to assign a new instance a name that’s already being used by another object, it might free the newly allocated instance and return the other object—thus ensuring the uniqueness of the name while at the same time providing what was asked for, an instance with the requested name.

In a few cases, it might be impossible for an init... method to do what it’s asked to do. For example, an initFromFile: method might get the data it needs from a file passed as an argument. If the file name it’s passed doesn’t correspond to an actual file, it won’t be able to complete the initialization. In such a case, the init... method could free the receiver and return nil, indicating that the requested object can’t be created.

Because an init... method might return an object other than the newly allocated receiver, or even return nil, it’s important that programs use the value returned by the initialization method, not just that returned by alloc or allocWithZone:. The following code is very dangerous, since it ignores the return of init.

id anObject = [SomeClass alloc];
[anObject init];
[anObject someOtherMessage];

Instead, to safely initialize an object, you should combine allocation and initialization messages in one line of code.

id anObject = [[SomeClass alloc] init];
[anObject someOtherMessage];

http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocAllocInit.html

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

哑剧 2024-10-11 01:45:08

init 方法不接收对象;该对象接收 init 消息。物体不拥有自己;相反,它总是了解自己(通过每条消息中隐式的 self 参数)。

不过,你说得对,该对象并不拥有自己。如果 allocinit 融合在一个 new 方法中,则该方法将是其自己的(super)调用者,因此它将拥有该对象(直到它返回),因此释放它无疑是正确的。由于它们是分开的,并且 init 不是 alloc 的调用者,因此您认为它不拥有该对象是对的,因此您对这种做法提出质疑是正确的。

这是一个对象可以代表另一个对象释放对象(在本例中为对象本身)的少数情况之一。另一种方法是不释放它,如果您要返回 nil 或抛出异常,这将是泄漏。

一般来说,任何时候你有一个对象保留或释放自己,你应该感到肮脏。 在这种特定情况下,这是可以的,因为您是在防止错误(泄漏),而不是可能创建错误。

2- 此外,在同一篇文章中,init 可以返回另一个对象或 nil。所以,在这种情况下,当我们使用alloc/init的完整模式时,我们无法释放alloc返回的对象,而只能释放init返回的对象,并且init代替我们释放从alloc接收的对象。

但是 init 不是一个 alloc、new、copy 或类似的方法,所以我们不能释放它返回的对象,因为它不给我们对象的所有权。

由于 init 代表其调用者释放旧对象,因此如果它创建一个新对象,它也会代表其调用者执行此操作。调用者确实拥有 init 为其创建或检索的替代对象。

由此推论,如果 init 检索到先前存在的对象,它必须保留该对象,以便调用者拥有它。

再次检查假设的* new 方法,它还需要释放旧对象并创建(拥有)或保留替代对象。

在所有这些情况下,它都是 init 代表其调用者行事。通常,一种方法执行另一种方法的内存管理是不可靠的,但对于这些情况,init 代表其调用者执行此操作是必要的。

*new方法确实存在,但只是发送allocinit,所以不需要单独实现它。

The init method doesn't receive the object; the object receives the init message. The object does not possess itself; rather, it always knows about itself (through the implicit self argument in every message).

You're right that the object does not own itself, though. If alloc and init were fused in a single new method, that method would be its own (super) caller, so it would own the object (until it returns) and so be unquestionably right in releasing it. Since they are separate, and init is not the caller of alloc, you're right that it does not own the object, so you are right to question this practice.

This is one of the few cases where it's OK for one object to release an object (in this case, itself) on behalf of another. The alternative is not to release it, which, if you're going to either return nil or throw an exception, will be a leak.

In general, anytime you have an object retain or release itself, you should feel dirty. In this specific case, it's OK, because you are preventing a bug (a leak) rather than probably creating one.

2- Also, from the same article, init could return another object or nil. So, in this case, when we use the complete pattern of alloc/init, we could not release the object returned by alloc but we could only release the object returned from init and, init releases the object it received from alloc instead of us.

But init is not a alloc,new,copy or alike method so we must not release the object returned from it as it does not give us the ownership of object.

As init releases the old object on behalf of its caller, if it creates a new object, it does that on behalf of its caller. The caller does own the substitute object that init creates or retrieves for it.

As a corollary to this, if init retrieves a previously existing object, it must retain that, so that the caller will own it.

Again examining the hypothetical* new method, it would also need to both release the old object and create (owningly) or retain the substitute.

In all of these cases, it's init acting on behalf of its caller. It's normally dodgy for one method to do another's memory management, but for these cases, init doing it on behalf of its caller is necessary.

*The new method does exist, but simply sends alloc and init, so there's no need to implement it separately.

£冰雨忧蓝° 2024-10-11 01:45:08

如果初始化由于某种原因失败并且必须返回 null,则必须释放该对象以避免内存泄漏。

类似地,init 可能决定交换不同的对象并返回它 - 在这种情况下,您还必须释放该对象以避免内存泄漏。

在这两种情况下,这是必要的,因为 init 不会返回原始对象,并且在方法返回后将成为孤立对象。 Alloc 会自动保留该对象,因此如果不释放它,其保留计数将永远停留在 1。

If initialization fails for some reason and must return null, then you must release the object in order to avoid leaking memory.

Similarly, init may decide to swap in a different object and return it - in that case you must also release the object in order to avoid leaking memory.

In both cases it's necessary because the original object isn't being returned by init, and will be orphaned after the method returns. Alloc has automatically retained the object, so if you don't release it its retain count will be stuck at 1 forever.

女中豪杰 2024-10-11 01:45:08

[换一个角度会有帮助吗?]
init 方法(及其兄弟 initWith... 和类似方法)有点奇怪,但不是内存分配规则的特殊情况。 Init 很奇怪,因为它的名称听起来像是要更改实例的内部结构,但实际上它可能做的不止于此(它可能会替换一些其他对象并初始化那个对象,例如例子)。提示位于 init 的声明中:

- (id)  init  // the way it is

vs

- (void) init  // in some other universe

init 方法返回一个对象,因此它可能被更好地命名为“返回一个(类方面)等效对象且已初始化的对象”。大多数方法不执行这种切换,这使得 init 有点不同/奇怪。

alloc / init 嵌套没有什么“魔力”——它只是处理从 alloc 返回的对象可能与从 init 返回的对象不同这一事实的最简单方法。这工作得很好:

NSSomeClass* s = [NSSomeClass alloc];
s = [s init];  // that 's =' part is really important ;-)

并且完全等同于“标准”习惯用法:

NSSomeClass* s = [[NSSomeClass alloc] init];

这可能是有问题的:

NSSomeClass* s = [NSSomeClass alloc]
[s init];   // bad! you ignored the result of init

当实现返回一个与作为传入“self”接收的对象不同的对象时,必须特别小心地执行 init 方法的实现。在这种情况下, init 方法承担“自身”对象的内存管理责任(因为它不会返回该对象 - 那么还有谁可以进行管理?)

有可能做一些相当丑陋的诡计, 顺便提一句。不要在家里尝试这个!

// don't do this!
S* s = [S alloc] 
[s retain]; // needs to survive 2 init's
S* ss = [s init......];  // s.retain goes 2-->1
S* sss = [s init.....];  //  ... goes 1-->0;

这样做是一种非常糟糕的做法,因为它依赖于 init..... 方法总是返回一个新对象(而不是它接收到的对象)。这显然是一个错误的假设!

请注意,“在‘self’中接收对象的方法”是指该方法是由对象调用的,并且按照惯例,该对象通过“self”指针可用。

[Would another perspective help?]
The init method (and its siblings initWith... and similar) is a bit of an odd case but is not a special case of memory allocation rules. Init is odd because it has a name that sounds like it is going to change the internals of the instance but in fact it may do more than that (it may substitute some other object and initialize that object, for example). The tip-off is in the declaration of init:

- (id)  init  // the way it is

vs

- (void) init  // in some other universe

The init method returns an object, so it might have been better named something like 'return an object that is (class-wise) an equivalent object and that has been initialized'. Most methods do not perform this kind of switcheroo, which makes init a bit different/odd.

There is nothing 'magic' about the alloc / init nesting -- it's just the simplest way to handle the fact that the object that you get back from alloc may not be the same object you get back from init. This works perfectly fine:

NSSomeClass* s = [NSSomeClass alloc];
s = [s init];  // that 's =' part is really important ;-)

and is exactly equivalent to the 'standard' idiom:

NSSomeClass* s = [[NSSomeClass alloc] init];

This is potentially problematic:

NSSomeClass* s = [NSSomeClass alloc]
[s init];   // bad! you ignored the result of init

The implementation of an init method must be done particularly carefully when the implementation returns a different object than the one it receives as the incoming 'self'. In such a case the init method takes on the responsibility of memory management of the 'self' object (because it's not going to return that object - so who else could be expected to do the management?)

It's possible to do some pretty ugly trickery, BTW. Don't try this at home!

// don't do this!
S* s = [S alloc] 
[s retain]; // needs to survive 2 init's
S* ss = [s init......];  // s.retain goes 2-->1
S* sss = [s init.....];  //  ... goes 1-->0;

Doing this is extremely poor practice because it depends on the init..... method always returning a new object (instead of the one it receives). That's an obviously bad assumption!

NOTE that by "a method receiving an object in 'self'" I mean that the method was invoked upon/by an object, and that object is made available by convention through the 'self' pointer.

×眷恋的温暖 2024-10-11 01:45:08

基本规则不适用于这种特殊情况。

事实上,不用担心 - 除非您打算进行 Posing,否则您不需要编写执行此操作的代码,并且它不会对您编写的代码产生任何影响。

您应该继续遵循所有代码中的基本规则。

The fundamental rule just doesn't apply in this special situation.

In fact, don't worry about it - unless you plan to do Posing, you won't need to write code that does this, and it will have no impact at all on the code you write.

You should continue to follow the fundamental rule in all your code.

人心善变 2024-10-11 01:45:08

第三,代码更像是您所说的“指南”,而不是实际规则。

(Barbosa 队长)

alloc/init 是一个特殊情况。您必须在 init 内部执行保留/释放操作,以便调用者返回的任何对象都归调用者所有,并且不会泄漏。

And thirdly, the code is more what you'd call "guidelines" than actual rules.

(Captain Barbosa)

alloc/init is a bit of a special case. You have to do the retain/release stuff inside init in such a way that whatever object the caller gets back is owned by the caller and there are no leaks.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文