【问题标题】:What is the new pattern for releasing self with automatic reference counting?使用自动引用计数释放 self 的新模式是什么?
【发布时间】:2012-04-22 02:46:49
【问题描述】:

以 NSObject 方法-(id)awakeAfterUsingCoder:(NSCoder *)decoder 为例,文档说:

允许一个对象在被解码后替换另一个对象 为自己。例如,表示字体的对象可能会在 被解码,释放自己并返回一个现有的对象 与自身相同的字体描述。这样,冗余对象可以 被淘汰了。

通常你会这样做

[self release];
return substitutedObject;

使用 ARC,您必须省略此行。这不会泄露吗?或者我应该相信 NSCoder 对象为我释放原始对象?如果是这样,为什么您必须首先使用非 ARC 代码显式释放 self?

根据编译器文档对 self 的描述,我认为 self = nil 不正确:http://clang.llvm.org/docs/AutomaticReferenceCounting.html#misc.self

【问题讨论】:

    标签: objective-c memory-management automatic-ref-counting nscoder


    【解决方案1】:

    类似的问题出现在 Mac OS X 上的 NIB 顶级对象的上下文中。Resource Programming Guide 说:

    如果文件的所有者不是NSWindowControllerNSViewController 的实例,那么您需要自己减少顶级对象的引用计数。使用手动引用计数,可以通过向顶级对象发送release 消息来实现这一点。 ARC 无法做到这一点。相反,您将对顶级对象的引用转换为 Core Foundation 类型并使用CFRelease

    所以,这种技术大概也可以用于这种情况。 CFRelease((__bridge CFTypeRef)self);

    【讨论】:

      【解决方案2】:

      如上所述,您不能写[self release];。此外,awakeAfterUsingCoder:不是初始化器——你不能重新分配self

      这不会泄露吗?

      是的。在下面的程序中证明。

      或者我应该只信任 NSCoder 对象为我释放原始对象?

      没有。

      下面存在一种避免泄漏的方法——我不会称之为“新模式”,只是想到的第一种方法。它涉及显式释放self,在这种情况下显式保留结果:

      #import <Foundation/Foundation.h>
      
      @interface MONBoolean : NSObject < NSCoding >
      
      - (id)initWithBool:(bool)pBool;
      
      - (bool)isTrue;
      - (bool)isFalse;
      
      @end
      
      static NSString * const MONBoolean_KEY_value = @"MONBoolean_KEY_value";
      
      @implementation MONBoolean
      {
          bool value;
      }
      
      - (id)initWithBool:(bool)pBool
      {
          self = [super init];
          if (0 != self) {
              value = pBool;
          }
          return self;
      }
      
      - (bool)isTrue
      {
          return true == value;
      }
      
      - (bool)isFalse
      {
          return false == value;
      }
      
      - (NSString *)description
      {
          return [[NSString alloc] initWithFormat:@"<%s:%p> : %s", object_getClassName(self), self, self.isTrue ? "true" : "false"];
      }
      
      - (void)encodeWithCoder:(NSCoder *)aCoder
      {
          [aCoder encodeBool:value forKey:MONBoolean_KEY_value];
      }
      
      - (id)initWithCoder:(NSCoder *)aDecoder
      {
          self = [super init];
          if (0 != self) {
              value = [aDecoder decodeBoolForKey:MONBoolean_KEY_value];
          }
          return self;
      }
      
      - (id)awakeAfterUsingCoder:(NSCoder *)aDecoder
      {
          const bool b = value;
          // cannot reassign self outside of an initializer.
          // if not released, will result in a leak:
          CFRelease((__bridge const void*)self);
          MONBoolean * result = [[MONBoolean alloc] initWithBool:b];
          // now we have to retain explicitly because this is
          // an autoreleasing method:
          CFRetain((__bridge const void*)result);
          return result;
      }
      
      @end
      
      int main(int argc, const char * argv[])
      {
          @autoreleasepool {
              MONBoolean * a = [[MONBoolean alloc] initWithBool:true];
              NSData * data = [NSKeyedArchiver archivedDataWithRootObject:a];
              MONBoolean * b = [NSKeyedUnarchiver unarchiveObjectWithData:data];
              NSLog(@"%@", b);
          }
          system("leaks NAME_OF_PROCESS_HERE");
          return 0;
      }
      

      【讨论】:

      • 你能解释一下为什么你必须明确地保留结果吗?这肯定违背了 ARC 的目的。感谢您回答其他所有问题。
      • @moshy 不客气。是的 - 我最初忽略了这个细节。原因是awakeAfterUsingCoder: 返回一个非拥有(或自动释放)的引用。因此,ARC 为我们的返回值插入了一个 ref-count 减量。我们想要做的是有效地将引用从一个对象转移到另一个对象。我在仪器中运行它——没有泄漏。没有僵尸。如果没有显式保留,则会向僵尸发送消息。没有明确的释放 - 泄漏。自己尝试一下(将@autorelease 块放在while (1) 中)。
      • 您能否将该方法设为init 家族的成员__attribute__((objc_method_family(init))),然后重新分配self
      • @HeathBorders 可能(试试看)。编译器使用什么来确定重新分配 self 是否有效需要确认属性。一般来说,属性不应该在这种情况下使用(imo),但如果苹果不允许这个错误漏掉就好了。您的解决方案将是他们可以采取的一种方法。
      • 我按照描述实现了Yang's solution 用于嵌入式 Nib 替换(使用 ARC),但是在使用手动释放时,旧的“自我”出现内存访问错误。我在NSObject.h 中看到以下内容,这让我怀疑对象替换现在由awakeAfterUsingCoder: 正确处理,无需手动释放和保留:- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder NS_REPLACES_RECEIVER;
      【解决方案3】:

      我相信 ARC 足够聪明,可以跟踪所有对象。因此,您应该能够对内存不说任何内容,并且应用程序将在不再使用该对象时释放该对象。通过泄漏分析器运行它,以防万一,但应该没问题。

      【讨论】:

      • 但我的理解是自动引用计数仍然只是引用计数。它与垃圾收集不同。因此,当它们不再使用时,仍然(理论上)可能有泄漏的对象。我将通过分析器运行它。
      猜你喜欢
      • 1970-01-01
      • 2014-08-02
      • 2013-05-15
      • 2011-09-15
      • 1970-01-01
      • 2017-01-15
      • 1970-01-01
      • 2011-09-17
      • 1970-01-01
      相关资源
      最近更新 更多