0

想象一下我的班级有以下 ivars/属性:

@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 发送消息)。我在这里写的似乎是这种初始化方式的唯一问题。你有什么意见?

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

pps。假设使用 ARC。

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

4

6 回答 6

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

于 2013-08-07T10:45:20.083 回答
1

我想您会在链接到的文档中找到答案:

重写 init 对于不需要额外数据来初始化其对象的子类来说很好。但通常初始化依赖于外部数据将对象设置为合理的初始状态。

因此,如果您的类在变量未初始化为适当值的情况下处于合理状态,则应使用

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

然后,init您可以使用默认值调用指定的初始化程序,或者如果没有合理的默认值,则不允许使用此处init描述的 SO

于 2013-08-07T08:31:17.927 回答
0

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

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

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

  • builder:当可能的参数太多或它们的组合很复杂时很有用。
  • “伸缩构造函数”</li>

主文件

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

营养事实.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
于 2013-08-07T11:00:12.480 回答
0

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

于 2013-08-07T08:02:27.557 回答
0

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

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

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

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

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

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

于 2013-08-07T09:27:59.733 回答
0

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

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

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

return self;

}

作为一种良好的编程习惯,您应该将对象初始化为 nil 以避免访问任何垃圾值。

于 2013-08-07T08:32:06.340 回答