作为iPhone / Objective-C开发的新手,我想问这个问题,以确保我在不同的场景中正确地初始化实例变量。所以下面,我将介绍几个场景,如果有人看到任何错误的做法,他们可以让我知道。 (注意:对于我的示例,我将使用“instanceVariable”作为我们要初始化的实例变量,这是“InstanceVariableClass”类的对象。)
场景1:在非UIViewController类中初始化
a)新分配
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
instanceVariable = [[InstanceVariableClass alloc] init];
}
return self;
}
在初始化程序中,可以直接访问变量(即不通过它的属性)并分配它。当您调用alloc时,将自动保留新创建的对象,这将在以后与getter和setter方法一起使用时完美地工作。您不希望使用属性分配变量,即self.instanceVariable = [[InstanceVariableClass alloc] init];
,否则您将保留两次(一次在setter方法中,一次在alloc中)。
b)参数
- (id)initWithFrame:(CGRect)frame object(InstanceVariableClass*) theInstanceVariable {
self = [super initWithFrame:frame];
if (self) {
instanceVariable = [theInstanceVariable retain];
}
return self;
}
再一次,可以直接访问初始化程序中的实例变量。由于您没有分配变量而只是想拥有传递给您的副本,因此您需要让它明确地保留自己。如果您已经使用了setter方法,那么它将为您保留它,但是希望避免访问初始化程序中的属性。
c)便利方法
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
instanceVariable = [[InstanceVariableClass returnInitializedObject] retain];
}
return self;
}
使用便捷方法返回新对象时,还需要显式保留,原因与参数相同。方便方法(如果正确实施)将自动释放它生成的新对象,因此我们不必担心双重保留它。
场景2:在UIViewController类中初始化
a)新分配
- (void) viewDidLoad // or - (void) loadView if you implemented your view programmatically
{
[super viewDidLoad];
InstanceVariableClass *tempInstanceVariable = [[InstanceVariableClass alloc] init];
[self setInstanceVariable: tempInstanceVariable];
[tempInstanceVariable release];
}
在UIViewController中,您希望在viewDidLoad方法中初始化实例变量,以使用延迟加载的练习,或仅在您需要的时刻加载变量。在初始化程序之外,直接访问变量是不好的做法,因此我们现在将使用我们的合成setter方法来设置变量。您不希望使用setter方法分配变量,即[self setInstanceVariable] = [[InstanceVariableClass alloc] init];
,否则您将保留两次(一次在setter方法中,一次在alloc中)。因此,最佳做法是创建一个新的临时变量,初始化临时变量,将实例变量设置为临时变量,然后释放临时变量。合成setter方法将为您保留变量。
b)便利方法
- (void) viewDidLoad // or - (void) loadView if you implemented your view programmatically
{
[super viewDidLoad];
[self setInstanceVariable: [InstanceVariableClass instanceVariableClassWithInt:1]];
}
在初始化方法之外初始化实例变量,我们可以简单地使用setter方法来设置和保留生成的对象。方便方法(如果正确实施)将自动释放它返回的对象,因此我们不必担心双重保留它。
这就是我到目前为止所拥有的。如果有人在我的推理中发现任何缺陷,或者想到我忘记包含的任何其他情况,请告诉我。感谢。
答案 0 :(得分:3)
1a)完美,除此之外:
自动呼叫保留
instanceArray
不保留自己 - 它只是为您的实例保留的原始内存的分配。
你认为正确的一个关键部分是许多人忽视的,你应该avoid using the accessors in partially constructed/destructed states。原因不仅仅是引用计数,它也是这些状态下正确的程序流程。
1b)将NSArray
属性声明为retain
极为罕见(对我来说) - 您应该使用copy
代替。 Your initializer should agree with the semantics of your property,所以在大多数情况下,您会将其更改为instanceArray = [parameterArray copy];
。
1c)看起来不错,但你也应该考虑我在1a和1b所做的观点。
2)嗯,这真的取决于。延迟初始化并不总是最好的。在某些情况下,最好在初始化程序中初始化ivars,有些则在加载视图时。请记住,您的vc可能已卸载,并且在加载时破坏您创建的内容非常典型。因此,确实没有一个硬性规则 - 如果某些事情需要时间来创建,或者必须在重新加载vc的情况下持续存在,那么在初始化程序中处理它可能更合乎逻辑。当延迟初始化是可取的时,这些示例看起来很好。
答案 1 :(得分:2)
您提供的所有示例都完全有效。
然而,许多有经验的obj-c程序员喜欢从不直接访问实例变量,除了在他们的set / get方法中(如果你用@property
声明它们,它们甚至可能不存在和@synthesize
),除非有必要避免一些性能瓶颈。
所以,我的构造函数通常看起来像这样:
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.instanceArray = [NSArray array];
}
return self;
}
但是我有时会选择完全按照你的方式编写我的代码,如果分析代码显示set / get方法并且autoreleass池占用了太多的CPU时间或RAM。
答案 2 :(得分:0)
首先,Objective-C没有类变量;只是实例变量。
其次,你过分思考这个问题。内存管理规则相对简单并且与setter / getter方法和/或对象创建正交。在-init*
方法中使用setter是一个问题,如果重写了setter,可能会触发副作用。但是,如果你在-init*
和-dealloc
期间出现了搞乱的setter / getter副作用,那么你可能会遇到更糟糕的架构问题。
如果你+ new,+ alloc,-retain或-copy [NARC]是一个对象,你需要 - 在某处释放它或者它会一直存在(并且可能会泄漏)。
如果一个setter想要保留一个对象,那么它要么 - 或者 - 对它进行复制(如果适用),那就是平衡-retain的业务。在外部设置者你不应该关心。
autorelease
只不过是每线程延迟release
。虽然通常不需要担心通过各种便利对象实例创建方法创建的autorelease
d个对象,但在某些情况下自动释放压力可能是一个真正的性能问题并使用显式+ alloc / set / - 发布可能很有用。
Objective-C memory management guide中详细解释了这一点。
这样想:
当您直接分配给iVar时,您不会离开调用范围,因此,分配可以消耗(可能)在调用范围内保留的+1保留计数。
通过方法调用(点语法或其他方式)分配时,在调用范围内维护的保留计数与该setter方法中发生的事件无关。这两者需要独立维持各自的保留计数增量。也就是说,如果setter想要保持对象周围,它将保留它。来电者保持独立的保留计数。
答案 3 :(得分:0)
Senario 1 a)
这是无用的代码。 NSArray是不可变的,因此一旦创建它就无法更改。所以请做instanceArray = nil;
或事件更好self.instanceArray = nil;
如果instanceArray是一个NSMutableArray,那么在那里分配它是有意义的,但是因为它不是,所以这是浪费。
1b)如果您的财产设置为(保留),请将其用于self.instanceArray = parameterArray
1c)这不是方便的方法。方便方法通常是返回自动释放对象的类方法 你在那里展示的代码,我确定它没有编译。
Senario 2a)
与1a)相同的答案
与1c相同的答案
尽可能使用您的财产。因此,如果你有变量那么多,那就更容易做到这一点 并确保了解NSArray和NSMutableArray之间的区别。 (或任何其他具有可变和不可变版本的类)
UIViewController和非UIViewController之间的区别
(好的,他们可以,但那时他们是nil
)
无法在init方法中访问IBOutlet。所以他们必须在以后初始化。
所以一般来说数据方面应该在init中完成,代码中的costomization应该进入viewDidLoad,而lastMinute逻辑和/或刷新应该进入viewWillAppear。
请记住,每次视图即将出现时都会调用viewWillAppear,包括从UIViewController层次结构中的较低位置返回时。
这是引导线,并且作为所有引导线,您有时需要稍微弯曲线。