【问题标题】:Objective-C multiple nested initWith for a custom class用于自定义类的 Objective-C 多重嵌套 initWith
【发布时间】:2012-12-09 08:42:50
【问题描述】:

我是 Objective-C 的新手,我有一个问题。

我创建了一个自定义类并尝试为初始化创建重载:

- (id)init
{
    if (self = [super init]) {
        [self setIsCurrentCar:NO];
    }
    return self;
}

-(id) initWithID:(NSInteger)id {
    if(self = [self init]) {
        [self setID:id];
    }
    return self;
}

-(id) initWithID:(NSInteger)id CarYear:(NSString *)year {
    if(self = [self initWithID:id]) {
        [self setCarYear:year];
    }
    return self;
}

假设有一次,我调用了-(id) initWithIDCarYear 方法。

我想知道上面的代码在结构上是正确的。

  • 在此代码中,self 设置了 3 次。有没有更好的 解决方案?
  • 此代码是否存在内存泄漏? (使用 ARC)
  • 我是否必须始终检查if(self = ...) 或者它是 冗余代码?

谢谢

@编辑 下面的代码更好吗?

-(id) initWithID:(NSInteger)id CarYear:(NSString *)year {
    if (self = [super init]) {
        [self setIsCurrentCar:NO];
        [self setID:id];
        [self setCarYear:year];
    }
    return self;
}

【问题讨论】:

  • 我担心内存泄漏的原因是我知道 [super init]、[self init] 和 [self initWithID:id] 创建了 3 个不同的实例,这意味着它们占用了 3 个记忆中不同的地方。使用 ARC,它们何时被释放?
  • 不,init 不会创建实例。只有alloc 可以。

标签: ios objective-c memory-leaks automatic-ref-counting


【解决方案1】:

虽然您的代码没问题,但我会以相反的顺序构造 init 调用,其中最详细的一个是指定的初始化程序,而更一般的调用会冒泡一些默认值:

-(id) initWithID:(NSInteger)id 
         CarYear:(NSString *)year 
{
    if(self = [super init]) {
        _year = year;
        _id = id;
    }
    return self;
}

-(id)initWithID:(NSInteger)id 
{
    return [self initWithID:id CarYear:@"unset"];
}

-(id)init 
{
    return [self initWithID:0];
}

如果调用更通用的初始化程序之一会产生非法状态,您可以改为抛出错误以禁止使用它。

让我们假设,汽车需要有一个 ID,但不是一年。使用initWithID 可以,但是使用init 会导致状态不一致,所以我们要强制不要使用它:

-(id)init 
{
    [NSException raise:NSInternalInconsistencyException 
                format:@"You must use -initWithID: or -initWithID:CarYear:", NSStringFromSelector(_cmd)];
    return nil;
}

  • 在此代码中,self 设置为 3 次。有没有更好的解决方案?

见上

  • 此代码是否存在内存泄漏? (使用 ARC)

没有,一切都很好

  • 是否必须始终检查 if(self = ...) 或者它是冗余代码?

正如我向您展示的那样:您可以在链中调用不同的 init 方法。只有该链中的最后一个需要执行该操作。


-(id) initWithID:(NSInteger)id CarYear:(NSString *)year {
    if (self = [super init]) {
        [self setIsCurrentCar:NO];
        [self setID:id];
        [self setCarYear:year];
    }
    return self;
}

您不应在 init 方法中对 self 使用 setter,see Apple's docs

【讨论】:

  • 这是 Objective-C 中的常见做法吗?我的意思是从下到上分解。我来自 C#,我可能会从上到下打破它,这意味着具有更多细节的重载将调用具有较少细节的重载。
  • 这样可以避免代码重复。 C# 可能有所不同,因为它区分了方法和构造函数。但是在 ObjC 中,init 仍然是正常的方法。对于方法,我总是使用默认参数调用更详细的方法。
  • 所以,即使我使用@synthesize IsCurrentCar 而不是@synthesize IsCurrentCar = _IsCurrentCar,属性也应该设置为_IsCurrentCar = NO;
  • 历史原因:苹果多年来一直建议不要使用下划线,所以很多开发者没有使用它。但是苹果改变了主意,如果没有找到综合语句,则首选下划线并综合。如果您要编译旧时代的源代码,当需要合成语句并使用现代编译器生成具有相同名称但不带下划线的支持变量时,并且自动合成和显式合成的默认值相同(前缀 _ ),这些代码会中断,因为直接访问 ivars 会产生 identifier not found
  • 顺便说一句:您应该熟悉 Cocoa 编码样式。变量名、对象名、方法名应为驼峰式,以小写字母开头。
【解决方案2】:

我想知道上面的代码在结构上是正确的。

是的。我看不出有什么问题。

在这段代码中,self 被设置了 3 次。有没有更好的解决方案?

这很正常。我不会费心去改变它。

这段代码有内存泄漏吗? (使用 ARC)

没有。

我是否必须始终检查 if (self = ...) 或者它是多余的 代码?

您不必必须,但绝对应该。详情请见this question

【讨论】:

  • 关于 if(self = ...),据我了解,每个 init 都会返回一个新实例,因此我们必须始终将其设置为 self。对吗?
  • 没有。类方法alloc 创建(分配)了一个新实例。 Init 只是设置它(设置实例变量的默认值等)。您必须将返回值设置为 self,因为在某些情况下,init 可能会返回另一个实例或 nil,以防初始化不成功。
【解决方案3】:

看起来您有一个强制初始化变量和两个实际上是可选的。

我建议为 ID 和 carYear 实现一个 init 方法和两个 @property()。这减少了初始化器的数量并更好地反映了类的使用约定。

【讨论】:

    【解决方案4】:

    据我所知...tried to create overloads for Initialization 此处不应使用此语句。

    通常overload 表示同名多个参数,但在 obj-c 中我们不遵循这一点。在 obj-c 中,通过命名参数来伪造重载。

    所以这里有 3 组不同的代码,每组都称为其他方法。而且您没有内存泄漏,因为您没有为同一个对象分配三次内存,而是在初始化它。

    【讨论】:

    • 是的,你是对的。 Objective-C 中没有重载,因为参数是方法名称的一部分,因此没有两个方法可以具有相同的名称。
    • 你的疑惑已经被viki和鼓手解决了:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-07
    • 1970-01-01
    • 1970-01-01
    • 2012-11-18
    • 1970-01-01
    • 1970-01-01
    • 2012-05-08
    相关资源
    最近更新 更多