IBOutlets,实例变量和属性:最佳实践

时间:2011-05-24 16:34:52

标签: objective-c ios memory properties

我今天做了各种关于声明IBOutlets和实例变量,管理它们,使用正确访问器并正确释放它们的最佳实践的研究。我几乎就在那里,但我有一些利基问题,我希望有人能够建议最好的做法。我将它们格式化为代码并对问题进行评论,以便更容易理解。我已经排除了一些我认为不相关的明显部分,可以安全地假设它们起作用(比如预处理器的东西,@ end,必需的实现方法等)。

MyViewController.h

@class OtherViewController;

@interface MyViewController : UIViewController {

     NSString *_myString;
     BOOL _myBOOL;

}

// The first two properties aren't declared in the interface
// above as per best practices when compiling with LLVM 2.0

@property (nonatomic, retain) OtherViewController *otherViewController;
@property (nonatomic, retain) UIButton *myButton;
@property (nonatomic, copy) NSString *myString;
@property (readwrite) BOOL myBOOL;

MyViewController.m

@implementation MyViewController

// Synthesizing IBOutlets on iOS will cause them to be
// retained when they are created by the nib

@synthesize otherViewController;
@synthesize myButton;

// Assign instance variables so as to force compiler
// warnings when not using self.variable

@synthesize myString = _myString;
@synthesize myBOOL = _myBOOL;

- (void)viewDidLoad {

     // QUESTIONS:

     // 1. Ignoring convenience methods, can you still alloc and init in dot notation
     //    even when it's being properly synthesized?

     self.myString = [[NSString alloc] initWithString:@"myString"];
     self.myString = existingNSStringObject;

     // 2. Should you always call methods for IBOutlets and instance variables using dot notation?
     //    Is there any difference seeing as these aren't directly invoking setters/getters?

     [self.myButton setText:self.myString];
     [myButton setText:self.myString];

     [self.otherViewController.view addSubview:mySubview];
     [otherViewController.view addSubview:mySubview];

     [self.myButton setAlpha:0.1f];
     [myButton setAlpha:0.1f];
     self.myButton.alpha = 0.1f;
     myButton.alpha = 0.1f;

     // 3. How fussy are scalar variables in terms of getters and setters,
     //    given that there is a @synthesize declaration for them?

     self.myBOOL = YES;
     myBOOL = NO;

     if(self.myBOOL) { ... }
     if(myBOOL) { ... }

     // 4. On instantiation of new view controllers from NIBs, should you use
     //    dot notation? (I haven't been doing this previously).

     otherViewController = [[OtherViewController alloc] initWithNibName:@"OtherView" bundle:nil];
     self.otherViewController = [[OtherViewController alloc] ... ]

}

- (void)viewDidUnload {

     // 5. Best practice states that you nil-value retained IBOutlets in viewDidUnload
     //    Should you also nil-value the other instance variables in here?

     self.otherViewController = nil;
     self.myButton = nil;

     self.myString = nil;

}

- (void)dealloc {

     [otherViewController release];
     [myButton release];
     [_myString release];   

}

3 个答案:

答案 0 :(得分:3)

1)你有点误解了@synthesize。 @synthesize对该对象不起任何作用。它只告诉编译器根据@property声明中使用的选项生成getter和setter方法

  

//在iOS上合成IBOutlets会   导致他们

     

//当他们保留时   由笔尖创建

不保留 outlet (出口只是界面构建器的注意事项而不影响代码),当@生成的setter时,对象会被保留使用合成。加载nib时,加载系统会调用生成的setter。

2)决定是否在目标C中使用访问器与决定在任何其他面向对象语言中使用访问器没有什么不同。它是风格,需求和坚固性的选择。访问者作为IBOutlet使用没有任何区别。

但是在目标C中,我建议你不要在两个地方使用访问器:dealloc和var的accessor方法本身。

如果你在init中使用访问器,那么你需要注意你的保留计数。

self.myString = [[NSString alloc] initWithString:@"myString"];

此行泄漏内存。使用您的副本访问器会保留该对象,因此您应该在创建它之后将其释放。

3)不确定你的意思是挑剔。可能会看到答案2)

4)参见2)并注意内存管理。如果你调用alloc / init,你现在负责释放对象 - 这完全独立于访问器和dealloc使用的保留/释放。

5)不,你不应该在viewDidUnload中忽略其他实例变量。即使视图消失,您的控制器仍应保持其状态。 viewDidUnload仅用于在控制器的视图当前不在屏幕上时清理可能占用大量内存的视图对象。

考虑导航控制器。视图控制器1在堆栈上,然后视图控制器2被按下并且现在可见。如果内存条件变低,系统可能会尝试卸载视图控制器1的视图,然后调用viewDidUnload。

然后弹出视图控制器2将不再创建视图控制器1对象,但它将加载视图控制器1的视图并调用viewDidLoad。

重新评论

2)这是完全正确的 - 你可以使用方便构造函数或在你的alloc / init和赋值之后立即释放,或者在块退出之前释放,或者自动释放。你选择的主要是风格问题(虽然有些人反对自动释放 - 但不是我!)

3)有标量的访问者 - 你已在代码中创建了一些

@property (readwrite) BOOL myBOOL;

这会在你的类上创建方法myBOOL和setMyBOOL。

请记住,点符号没有什么特别之处。这只是一种方便,当编译代码时,myObject.property与[myObject property]完全等效,myObject.property = x与[myObject setProperty:x]完全等效。使用点符号纯粹是一种风格选择。

答案 1 :(得分:3)

我总是声明并显式设置属性的底层实例变量。这是前面的一点工作,但在我看来,明确区分变量和属性并一目了然地看一个类有哪些实例变量是值得的。我还为实例变量名添加前缀,因此如果我不小心输入property而不是object.property,编译器会抱怨。

  1. 调用alloc / init会创建一个保留计数为1的对象。您的合成属性也将保留该对象,在释放时导致内存泄漏(除非您之后立即释放您的属性,但这是不好的形式) 。最好在单独的行上分配/释放对象。

  2. 点符号实际上与调用[self setObject:obj]相同。不使用点表示法直接访问底层实例变量。在initdealloc中,始终直接访问实例变量,因为访问器方法可以包括在创建或销毁对象时无效的额外操作(例如键值观察通知)。所有其他时间使用合成的访问器方法。即使您现在没有做任何特别的事情,稍后您可能会稍后覆盖这些方法以更改设置变量时发生的情况。

  3. Scalars以同样的方式工作,只有你不必担心内存。

  4. 一个访问合成的访问器方法,另一个访问实例变量。再看第一和第二个问题,并注意内存泄漏!

  5. 视图控制器可能会再次被推到屏幕上,在这种情况下,您的viewDidLoad方法将再次被调用。如果您要在viewDidLoad中设置初始值,请继续并在此处将属性设置为nil。这对于使用大量内存且不会影响视图状态的属性有意义。另一方面,如果您希望该属性持续存在直到您确定不再需要该属性,请使用init方法创建该属性,并且在dealloc之前不要将其释放。

答案 2 :(得分:1)

  1. 点符号和括号表示法几乎相同。
  2. 通过self.myVariable访问实例变量myVariable的属性的getter,并通过myVariable访问本地变量。他们不是一回事。
  3. 您可以通过覆盖方法和具体的某些条件来自定义setter和getter。
  4. 请参阅第一个答案(括号是首选 - 更好地理解代码)
  5. 更好地制作单独的方法。
  6. 像:

    - (void) releaseOutlets {
     self.firstOutlet = nil;
     self.mySecondOutlet = nil;
     ……………………
     self.myLastOutlet = nil;
    }
    

    然后在viewDidUnloaddealloc方法中调用此方法。

    希望它有所帮助!