代码之家  ›  专栏  ›  技术社区  ›  lockedscope L42

使用alloc和init

  •  6
  • lockedscope L42  · 技术社区  · 14 年前

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

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

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

    这怎么可能违背基本原则呢?

    同样,在同一篇文章中,init可以返回另一个对象或nil。因此,在这种情况下,当我们使用alloc/in it的完整模式时,我们不能释放alloc返回的对象,但是我们只能释放init返回的对象,init释放它从alloc而不是我们接收的对象。

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

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

    或者,为了遵守同一条的最后一款, 我们必须接受init方法作为特例,并使用alloc/init模式作为基本规则的异常吗?

    内存管理基本规则:

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

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

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

    因为一个初始化。。。 方法可能返回新分配的接收器以外的对象,甚至返回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 回复  |  直到 14 年前
        1
  •  8
  •   Peter Hosey    14 年前

    这个 init 方法不接收对象;对象接收 初始 信息。对象并不拥有自己;相反,它总是知道自己(通过隐式 self 每个消息中的参数)。

    不过,你说得对,物体本身并不属于自己。如果 alloc 初始 融合在一起 new 方法,这个方法是它自己的( super )调用者,所以它将拥有对象(直到它返回),因此无疑是正确的释放它。因为他们是分开的,而且 初始 不是的呼叫者 分配 ,你是对的,它不拥有对象,所以你质疑这种做法是对的。

    这是少数几种情况之一,其中一个对象可以代表另一个对象释放对象(在本例中是对象本身)。另一种选择是不释放它,如果你打算返回 nil 否则抛出异常,将是一个漏洞。

    一般来说,任何时候当你有一个对象保留或释放它自己时,你都应该感到脏兮兮的。 在这种特殊情况下 ,没关系,因为您正在防止一个bug(泄漏),而不是创建一个bug。

    同样,在同一篇文章中,init可以返回另一个对象或nil。因此,在这种情况下,当我们使用alloc/in it的完整模式时,我们不能释放alloc返回的对象,但是我们只能释放init返回的对象,init释放它从alloc而不是我们接收的对象。

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

    作为 初始 代表调用方释放旧对象,如果创建新对象,则代表调用方执行此操作。调用方确实拥有替换对象 初始 为其创建或检索。

    因此,如果 初始 检索先前存在的对象,它必须保留该对象,以便调用方拥有它。

    再次审视假设* 新的 方法,它还需要释放旧对象并创建(所有者)或保留替换对象。

    在所有这些情况下 初始 代表来电者行事。一种方法进行另一种方法的内存管理通常是不可靠的,但在这些情况下, 初始 代表来电者这样做是必要的。

    *那个 新的 方法确实存在,但只需发送 分配 初始 ,所以不需要单独实现。

        2
  •  3
  •   Sean U    14 年前

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

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

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

        3
  •  1
  •   Art Swri    12 年前

    [另一种观点会有帮助吗?] init方法(及其同级initWith。。。是一个有点奇怪的情况,但不是内存分配规则的特殊情况。in it很奇怪,因为它的名称听起来像是要改变实例的内部结构,但实际上它可能做的不止这些(它可以替换其他一些对象并初始化 那个 例如,对象)。提示在init的声明中:

    - (id)  init  // the way it is
    

    - (void) init  // in some other universe
    

    init方法返回一个对象,因此它最好命名为“return an object that is(class-wise)an equivalent object and that has been initialized”。大多数方法不执行这种switchero,这使得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方法的实现。在这种情况下,In it方法负责“self”对象的内存管理(因为它不会返回该对象,所以可以指望谁来管理?)

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

    // 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;
    

    这样做是非常糟糕的做法,因为这取决于初始化。。。。。方法总是返回一个新对象(而不是它接收的对象)。这显然是个糟糕的假设!

    注意,通过“一个在'self'中接收对象的方法”,我的意思是该方法是由一个对象调用的,并且该对象通过'self'指针按惯例可用。

        4
  •  0
  •   Alex Brown    14 年前

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

    事实上,不要担心它-除非你打算摆姿势,否则你不需要编写这样的代码,而且它对你编写的代码没有任何影响。

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

        5
  •  0
  •   JeremyP    14 年前

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

    (巴博萨船长)

    alloc/init有点特殊。您必须在init中执行retain/release操作,这样调用方返回的任何对象都归调用方所有,并且没有泄漏。