使用alloc和init

时间:2010-11-25 16:04:19

标签: objective-c cocoa memory-management initialization allocation

我们知道必须合并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方法作为特例并使用alloc / init模式作为基本规则的例外吗?

  

内存管理基本规则:

     
      
  • 您只释放或自动释放您拥有的对象。      
        
    • 如果您使用名称以“alloc”或“new”开头或包含“copy”(例如,alloc,newObject或mutableCopy)的方法创建对象,或者如果您发送它,则获取对象的所有权保留信息。
    •   
    • 您使用release或autorelease放弃对象的所有权。 autorelease只是意味着“将来发送一条发布消息”(了解这将是什么时候,请参阅“Autorelease Pools”)。   http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html
    •   
  •   
     

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

     

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

     

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

     

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

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

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

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

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

5 个答案:

答案 0 :(得分:8)

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,因此无需单独实施。

答案 1 :(得分:3)

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

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

在这两种情况下都是必要的,因为初始对象不是由init返回的,并且在方法返回后将被孤立。 Alloc已经自动保留了对象,所以如果你不释放它,它的保留计数将永远停留在1。

答案 2 :(得分:1)

[另一种观点有帮助吗?] init方法(及其siblings 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方法负责“自我”对象的内存管理(因为它不会返回该对象 - 所以还有谁可以进行管理?)

可能会做一些非常难看的诡计,BTW。不要在家里试试这个!

// 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”指针按惯例可用。

答案 3 :(得分:0)

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

事实上,不要担心 - 除非你打算做Posing,否则你不需要编写执行此操作的代码,它对你编写的代码完全没有影响。

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

答案 4 :(得分:0)

  

第三,代码更像是你所谓的“指南”而非实际规则。

(巴博萨船长)

alloc / init有点特殊。你必须在init中执行保留/释放内容,使得调用者返回的任何对象都由调用者拥有并且没有泄漏。