1

作为从 [[class alloc] init] 返回 nil 被认为是好的做法?,有一种情况我没有看到太多讨论:如何处理一个在调用下一个 init 之前失败了一些先决条件的 init?

例如,假设在这个 initWithStuff: 方法中被传递 nil 或者通常没有值传递给 initWithValue: 是绝对失败的,我们肯定想要返回 nil。

- (id)initWithStuff:(Stuff *)inStuff {
  if (!inStuff || ![inStuff hasValidValue])
  {
    // can't proceed to call initWithValue: because we have no value
    // so do what?
    return nil;
  }
  NSInteger value = [inStuff integerValue];
  return [super initWithValue:value];
}

也许一个更清楚的例子是,如果我们包装的指定初始化方法接受一个对象指针,如果它传递 nil 则抛出异常。我们肯定需要短路会导致异常的初始化调用。

我的猜测:以任何可能的方式初始化,然后才在返回零之前释放自我。如有必要,请调用裸 init 或任何其他初始化程序,以在释放它之前完成将 self 置于已知状态。

  // can't proceed to call super's initWithValue: because we have no value
  // so do what? do this:
  self = [super init]; // or initWithValue:0
  [self release];
  return nil;

如果没有这样的初始化程序可以在没有有效数据的情况下工作,我想人们需要构造一些有效的虚拟数据。或者向其作者投诉,然后返回 nil 并忍受泄漏:^)

另外,ARC如何影响这种情况?

我的猜测:仍然以任何可能的方式完成初始化,然后返回 nil。你会认为设置 self 可能是多余的,但在某些情况下并非如此。在任何情况下,它都需要在那里消除编译器警告。

  // can't proceed to call super's initWithValue: because we have no value
  // so do what? do this:
  self = [super init]; // finish init so ARC can release it having no strong references
  return nil;

我的猜测有任何错误吗?

4

1 回答 1

1

理想情况下,如果先决条件失败,则不要调用[super init…]. 您只需释放self(如果不使用 ARC)并返回 nil:

- (id)initWithStuff:(Stuff *)stuff {
    if (!stuff || ![stuff isValid]) {
        [self release]; // if not using ARC
        return nil;
    }

    if (self = [super init]) {
        // initialization here
    }
    return self;
}

该版本负责self在 MRC 下解除分配。在 ARC 下,编译器将为您插入发行版。

然而,这种方法存在一个潜在的问题。 当你释放self时(或当 ARC 为你释放它时),系统会将dealloc消息发送给对象。你的dealloc方法会调用[super dealloc]. 您可以抑制[super dealloc]MRC,但您无法使用 ARC 来避免它。

所以危险在于你的超类可能假设它的一个实例变量已经被初始化,并且依赖于它的dealloc. 例如,假设这是超类:

@interface SomeSuperclass : NSObject
@end

@implementation SomeSuperclass {
    CFMutableBagRef bag;
}

- (id)init {
    if (self = [super init]) {
        bag = CFBagCreateMutable(NULL, 0, &kCFTypeBagCallBacks);
    }
    return self;
}

- (void)dealloc {
    CFRelease(bag);
}

@end

这里的问题是CFRelease要求它的论点不是零。因此,如果您不调用[super init]子类,这将在释放期间崩溃。

鉴于这个问题,我不得不改变我最初的建议。如果您知道您的超类dealloc没有此类问题(例如,因为它会在取消引用或将指针传递给 之前检查指针CFRelease),那么您可以放心地不调用[super init].

如果您知道您的超类dealloc是安全的,那么我的建议是您将前提条件移出init并移入类工厂方法。

换句话说,不要将alloc/init其视为类的公共接口的一部分。提供创建实例的类方法:

// The class factory method.  Declare this in your header file.  This is how you
// or any user of this class should create instances.
+ (id)myObjectWithStuff:(Stuff *)stuff {
    if (!stuff || ![stuff isValid])
        return nil;

    // self here is the class object, so it's appropriate to send `alloc` to it.
    // You don't want to hardcode the class name here because that would break
    // subclassing.
    return [[self alloc] initWithStuff:stuff];
}

// This is now considered a private method.  You should not declare it in your
// header file, though in Objective-C you can't prevent the user from calling it
// if he's determined to.
- (id)initWithStuff:(Stuff *)stuff {
    // Precondition was already checked in myObjectWithStuff:.
    if (self = [super init]) {
        // initialization here...
    }
    return self;
}
于 2012-11-16T02:40:14.717 回答