对象初始化样式

时间:2013-08-07 07:52:58

标签: ios objective-c

想象一下,我的班级有以下ivars / properties:

@property (nonatomic, copy)   NSString *itemName;
@property (nonatomic, copy)   NSString *serialNumber;
@property (nonatomic) int     valueInDollars;
@property  NSDate *dateCreated; 

1)初始化此类的ivars的一种方法是这样的:

// Designated initializer of this class 
-(id) initWithItemName: (NSString*) name
      valueInDollars:(int)value
      serialNumber:(NSString *)sNumber
{

    // Call the superclass's designated initializer
    self = [super init];

    if(self)
    {
        // Init properties
        self.itemName =  name;          
        self.serialNumber = sNumber;      
        self.valueInDollars = value;       
        dateCreated = [[NSDate alloc] init];  
    }

    // Return the address of the newly initialized object
    return self;
}

2)我想的另一种方法是初始化这个类,例如是写:

-(id) init
{
 self = [super init];

    if(self)
    {
     // basically do nothing
    }

return self;

}

然后将留给将使用该类的用户根据需要进行初始化,例如,

MyClass *object = [[MyClass alloc] init];

object.dateCreated = [[NSDate alloc] init];
[object.dateCreated someMethod];
object.itemName = "Hello";
object.someProperty = [[SomeClass alloc] init];

我认为上面的内容是某些属性(如上所述)在被使用之前必须被称为alloc/init才不是吗?如果用户忘记这样做,那么最多该应用程序无法正常工作吗? (因为我们可以向nil发送消息,所以它不会崩溃)。我在这里写的似乎是这种初始化方式的唯一问题。你有什么看法?

PS。它也允许在这里:http://developer.apple.com/library/ios/#documentation/general/conceptual/CocoaEncyclopedia/Initialization/Initialization.html

PPS。假设使用了ARC。

感谢许多回复,但基本上我对解决方案2可能出现的问题感兴趣?

6 个答案:

答案 0 :(得分:1)

我想你会在the document that you linked to中找到答案:

  

对于不需要额外数据来初始化其对象的子类,覆盖init就可以了。但通常初始化依赖于外部数据来将对象设置为合理的初始状态。

因此,如果您的类未处于合理状态,如果变量未初始化为正确值,则应使用

- (id)initWithItemName:(NSString*)name valueInDollars:(int)value serialNumber:(NSString *)sNumber

然后在init中,您可以使用默认值调用指定的初始值,或者如果没有合理的默认值,则不允许使用here on SO所述的init

答案 1 :(得分:1)

我建议你创建一个调用init方法的工厂方法,以便在同一步骤中组合分配和初始化,并隐藏初始化的细节。

@interface CCAttachment()
@property (readwrite, strong, nonatomic) NSString *urlString;
@property (readwrite, strong, nonatomic) NSString *baseURLString;
@property (readwrite, strong, nonatomic) NSData *data;
@property (readwrite, strong, nonatomic) id object;
@property (readwrite, strong, nonatomic) AFHTTPClient *client;
@end

@implementation CCAttachment
//Init method
- (id)initWithURLString:(NSString *)aURLString baseURLString:(NSString *)aBaseURLString
{
    self = [super init];
    if (self)
    {
        self.urlString = aURLString;
        self.baseURLString = aBaseURLString;
    }
    return self;
}
//Factory method
+ (instancetype)attachmentWithURLString:(NSString *)aURLString baseURLString:(NSString *)aBaseURLString
{
    return [[self alloc] initWithURLString:aURLString baseURLString:aBaseURLString];
}

@end

它们将为创建实例提供更统一的界面。例如,如果您以后想要将上述对象转换为nsmanagedobject,则将保留相同的工厂方法并仅更改其实现

+ (instancetype)attachmentWithURLString:(NSString *)aURLString baseURLString:(NSString *)aBaseURLString
{
    CCAttachment *result = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass(self.class) inManagedObjectContext:MOC];
    result.urlString = aURLString;
    result.baseURLString = aBaseURLString;
    return result;
}

http://developer.apple.com/library/ios/#documentation/general/conceptual/CocoaEncyclopedia/ClassFactoryMethods/ClassFactoryMethods.html

答案 2 :(得分:0)

正如@Kreiri所说 - Depedns ......但我认为如果你想防止一些愚蠢的错误/错误,你应该给用户一个名为指定初始化程序的工具。崩溃的可能性越小,代码越好!

答案 3 :(得分:0)

您应该尝试自己初始化ivars以防止任何类型的崩溃。第一种方法限制用户输入所有参数,并且是安全的。第二种方法可能会导致您自己提到的崩溃,但如果您在第二种方法中自己初始化变量,则可以保证安全。

-(id) init
{
 self = [super init];

    if(self)
    {
     itemName = @"";
    serialNumber = @"";
    valueindollars = -1;
    dateCreated = nil;//Better than garbage
    }

return self;

}

作为一种优秀的编程实践,您应该将对象初始化为nil以避免访问任何垃圾值。

答案 4 :(得分:0)

您应该熟悉面向对象设计的基础知识。

通常,类的实例在创建后应满足明确的不变。这意味着,您的实例(包括所有ivars)在初始化后应处于特定的逻辑正确状态

还需要响应任何消息之后,实例必须仍然履行其不变量。此状态可能与响应消息之前不同,但它必须仍处于合理状态。

形成第二设计,用户可以随时通过属性设置和阅读任何ivar。现在假设,您的实例还会响应您在类中定义为方法的其他消息。

现在回答这个问题:是否保证在任何时候你的类的实例处于满足不变条件的状态,因此总能以明确和明确的方式响应任何消息?

你会意识到,在第二种方法中,只有当你的班级只是作为四个ivars的容器时才会出现这种情况,并且实际上没有其他责任。那么,应该问一下课程的目的是什么呢? ;)

答案 5 :(得分:0)

作为一般原则,创建具有无效状态的对象是不好的方式,因为用户可能忘记在使用某个变量之前必须为某个变量设置有效状态

有时会有太多可能的参数,这会使经典模式(有时称为“伸缩构造函数”)变得繁琐。想想披萨配料,例如initPizzaWithPepperoni:tomate:mozzarella:...等。对于这种情况,您可以使用构建器模式。

“Effective Java”中的这个示例说明了初始化对象的三种不同方法

  • builder:当有太多可能的参数或它们的组合很复杂时很有用。
  • bean
  • “telescoping constructor”

<强>的main.m

int main(int argc, char *argv[]) {
    @autoreleasepool {
        NutritionFacts *facts = [NutritionFacts new];

        // builder
        facts = [[[[facts builder] calories:100] sodium:35] build];

        // bean constructor pattern
        facts.calories=100;
        facts.sodium=35;

        // telescoping constructor pattern
        // [[NutritionFacts alloc] initWithCalories:100];
        // [[NutritionFacts alloc] initWithCalories:100 sodium:35];
    }
}

<强> NutritionFacts + Builder.m

@interface NutritionFacts(Builder)
-(NutritionFactsBuilder*)builder;
@end

@implementation NutritionFacts(Builder)
-(NutritionFactsBuilder*)builder {
    return [[NutritionFactsBuilder alloc]initWithNutritionFacts:self];
}
@end

<强> NutritionFacts.m

@interface NutritionFacts : NSObject
@property (nonatomic, assign) NSUInteger calories, carbohydrate,
cholesterol, fat, fiber, protein, saturatedFat, sodium;
@end

@implementation NutritionFacts
@end

<强> NutritionFactsBuilder.m

@interface NutritionFactsBuilder : NSObject
-(id)initWithNutritionFacts:(NutritionFacts*)facts;
@end

@implementation NutritionFactsBuilder {
    NutritionFacts *_facts;
}
-(id)initWithNutritionFacts:(NutritionFacts*)facts {
    self = [super init];
    if (self){
        _facts = facts;
    }
    return self;
}
-(BOOL)isValid {
    // ... check valid ivar combos
    NSAssert(YES,@"Always valid");
}
-(NutritionFacts*)build {
    [self isValid];
    return _facts;
}
-(instancetype)calories:(NSUInteger)calories {
    _facts.calories = calories;
    return self;
}
-(instancetype)sodium:(NSUInteger)sodium {
    _facts.sodium = sodium;
    return self;
}
// ...
@end