【问题标题】:custom class initializers with ARC - self.name or name?使用 ARC 的自定义类初始值设定项 - self.name 还是 name?
【发布时间】:2012-07-28 18:30:53
【问题描述】:

使用 ARC 定义 Person 类时,

  1. 我应该在初始化程序中使用 self.fullname 还是只使用 fullname
  2. 如果我使用fullname,传递的字符串会被保留吗?
  3. 如果我使用 self.fullname 我必须定义一个 setter 或一个属性?我应该使用strong吗?

来自 ARC 之前的思维方式,我正试图围绕 ARC 建议的变化来思考。

【问题讨论】:

    标签: objective-c ios5 memory-management


    【解决方案1】:
    1. 对于 ARC,它们的行为方式相同,除了 self.fullname 将通过设置器。默认设置器将为您提供 KVO 合规性。但除此之外,没有区别。
    2. 是的,如果指针被声明为强指针,它们会。
    3. 要使用self.fullname = ...,您必须定义一个setter。对于 NSString 和其他具有可变变体的类,通常建议使用(copy)

    【讨论】:

    • 所以你会推荐:“@property (copy) NSString *fullName;”作为财产?
    • 是的,这行得通。如果您希望值可以从不同的类访问(否则,不需要它),通常属性是一个好主意。
    • 属性(= 带有 getter 和 setter 的合成访问器)总是一个好主意,即使您不从外部访问它们。
    • @auco 你为什么这么说?如果您不需要外部访问并且不需要 KVO 合规性,那么属性/访问器只会增加额外的开销。我不相信它们是线程安全的。
    • @jtbandes:当然它们可能是线程保存的(当然取决于对象实现),这就是您可以指定(原子/非原子)的原因。属性主要帮助你节省大量的打字。如果您考虑一个有 500 多行的类,那么(从概念上讲)您是从内部还是外部访问您的属性/变量没有区别。有可能把它搞砸。访问器只是一个好习惯。我怀疑有很多用例调用几个额外的访问器方法的性能开销比应用良好的编程实践更好。
    【解决方案2】:

    在初始化程序中,我建议反对调用self 上的任何方法,因为该对象处于这种不寻常的状态,它缺乏自我一致性。对于您给出的简单示例,它不会立即产生影响。但是,如果您稍后定义自己的 -setFullname: 方法来读取或写入对象的任何其他部分,则从您的初始化程序中调用 self.fullname = 会导致问题,因为该对象尚未完全形成。

    【讨论】:

    • 但如果我将 iVar 的属性设置为复制。除非我使用 self 否则它不会复制 - 或者会吗?
    • 自己动手吧:fullname = [name copy];
    【解决方案3】:

    我认为可用的答案需要澄清一下。

    对您问题的详细回答

    看看这个标题:

    Person : NSObject {
        NSString *name; // better to call this _name to not confuse it with the property
        // and even more better to not use an ivar, but only a property
    }
    
    @property (strong) NSString *name;
    
    1. 您有一个实例变量。通常您会使用下划线将您的 ivar 与属性称为相同的名称,但通常您会发现 ivar 和属性具有相同的名称。
    2. 也可以声明一个属性,在这种情况下,编译器会自动为你插入一个带下划线的ivar!

    现在重要的是要了解,name = @"John Smith"self.name = @"John Smith" 之间有一个主要区别,第一个直接设置实例变量(又名 _name = @"John Smith",忽略内存管理和(没有 ARC)创建泄漏如果前一个值不是 nil。使用 self-dot-syntax (self.name) 使用 自动生成的访问器(=setter 方法),它尊重所选的内存管理(通常是保留或强或复制)。

    在属性和 ARC 之前,对象设置器看起来像这样:

    -(void)setName:(NSString*)newName {
        if(newName != name) {
            [name release];
            name = newName;
            [newName retain];
        }
    }
    

    这意味着,旧的 iVar 值被释放并保留新的 iVar 值。所有的平衡和罚款。

    现在,有了 ARC 和综合访问器(属性),您就不必关心所有这些了。属性综合存取器,而 ARC 根据对代码的分析综合和平衡保留/释放调用。因此 ARC 和属性不一定相互要求,因为它们综合了不同的方面(例如,请注意 ivar 声明中的不同语法“__weak”和属性声明中的“(weak)”?)。但是了解它过去是如何工作的很有用,因为现在您会发现两者之间存在重大差异

    name = @"John Smith"; // directly access ivar, the old value is never released 
    // your program is leaking if you're not using ARC
    

    并通过合成访问器

    self.name = @"John Smith"; // old ivar released, new ivar set, all okay
    

    对您的问题的简短回答

    1. 不要使用 ivar 如果两者都不需要,则不要使用属性
    2. self.property = newValue 通过其 setter 方法设置新值
    3. ivar = newValue 直接设置新值,绕过任何 setter 方法
    4. 如果有 setter 方法,绝对建议使用访问器并且不要直接设置 ivars(换句话说:如果你有一个属性,通过调用 self.property = newValue 来使用它的 setter)

    【讨论】:

    • 很高兴知道编译器会自动为我插入一个带下划线的 ivar。我不知道。关于我的问题 - 我想知道 ARC 如何直接访问 iVar。我的意思是,如果我创建一个这样的新人员类: [[Person alloc] initWithName:@"James"] - 这意味着 @"James" 字符串是自动释放的 - 我没有保留它......我理解两者之间的区别self.name 和 name,我猜在初始化程序中它不太重要,因为没有以前的值。
    • @Guy:通常,您会期望 -initWithName: 使用 self.name=@"James"。它是否是初始化器并不重要,这是谁“拥有”对象的问题(所有者在创建 obj 时保留并在销毁它时释放;其他人保留并自动释放其值)。但是:幸运的是,有了 ARC,您就不必再担心这个问题了。作为一般规则:如果有属性,请使用它。不要直接访问 ivar,不尊重 setter。 AFAIK ARC 不处理 ivars:属性合成访问器,ARC 合成和平衡保留/释放调用。
    猜你喜欢
    • 2012-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-29
    • 2020-09-21
    • 2019-11-28
    • 1970-01-01
    • 2011-12-09
    相关资源
    最近更新 更多