【问题标题】:Objective-C init quirksObjective-C 初始化的怪癖
【发布时间】:2020-05-04 13:35:29
【问题描述】:

在类接口中我定义了一些 ivar

@property (strong,nonatomic) id < Protocol > initEst;   // Initial estimate

这编译没有问题,但是当我运行程序时,它崩溃了,原因是调试器指出了 EXC_BAD_ACCESS[Object .cxx_destruct]

发生了什么事?

【问题讨论】:

    标签: objective-c automatic-ref-counting initializer autorelease


    【解决方案1】:

    我已经对此进行了更多测试,并且似乎有 三个 条件可以显示这个特殊的怪癖。

    在我的特殊情况下,ivar 的 Protocol 也与包含类的相同。这似乎是这个问题浮出水面的额外要求(参考我之前没有提到这一点的答案)。

    所以要详细说明我之前的回答。如果

    1. initXXX 是一个 ivar
    2. id 类型
    3. 实现与包含类相同Protocol

    那么 Objective-C + ARC 编译器会很高兴地编译代码但无法执行。

    这是我用来测试的代码示例

    @interface Dog : NSObject < Animal >
    
    @property (nonatomic,strong) id < Animal > initState;
    
    @end
    

    这样的事情会因为名称以 init 开头而引起问题。换个名字,所有问题都消失了。

    作为参考,这里生成的运行时错误是

    Dog 对象在释放时过度释放

    这个 sn-p 非常抽象,但这可能会在您需要指定一些初始条件以及很自然地命名一些 ivar initXxx 但要注意,如果您使用 Objective-C,您没有这种奢侈,编译器也不会警告你这是错误的。

    最初的错误似乎与内存分配有关,让我怀疑我使用 autoreleasepool 的方式,但现在我确信这与问题无关。

    【讨论】:

      【解决方案2】:

      这完全是关于 ARC 自动内存管理的规则。初始化器对如何处理返回值有特殊的规则:它保留和返回。见https://clang.llvm.org/docs/AutomaticReferenceCounting.html#semantics-of-init

      一般来说,Objective-C,尤其是 ARC,对方法名称的含义有一些非常严格的规则。 initXXX 表示“这是一个初始化程序”。如果这不是初始化程序,请不要使用 init 前缀。

      您可以完全关闭 ARC 并自己管理内存,但仅遵守约定会更容易,并且更适合与其他语言(例如 Swift)交互。

      【讨论】:

      • 我接受init 的麻烦,但即使是链接init 也只与方法相关联。我根本没有使用任何方法,并且对它被这样对待感到惊讶。
      • 如果我不使用 ARC,我可以保留并自动释放它。我想具体了解当您在 autorelease 内部分配和在 autorelease 外部使用时,ARC 和 autoreleasepool 是如何协同工作的。
      • @skaak self.initEst = ...[self setInitEst:...] 相同。
      • 如果initEst指向一个普通对象,编译器会抱怨init部分但是如果它是一个id指针那么它不会抱怨,所以你只能做self.initEst =[self setInitEst:...] 如果它是 id 指针。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-22
      • 2013-03-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多