首页 > 代码库 > iOS 6 By Tutorials ---第二章--【第二弹】--【翻译】

iOS 6 By Tutorials ---第二章--【第二弹】--【翻译】

Fun with instance variables
实例变量的乐趣
 
Take a look at MasterViewController.h:——看看 MasterViewController类
@interface MasterViewController : UIViewController <UITableViewDataSource, UITableViewDelegate,DetailViewControllerDelegate> {// Instance variables for outletsUITableView *tableView; UISegmentedControl *segmentedControl;... }@property (nonatomic, strong) IBOutlet UITableView *tableView; @property (nonatomic, strong) IBOutlet UISegmentedControl*segmentedControl;...@end

Usually when you declare a property, you want to have it “backed” by an instance variable that stores the actual value for that property. For example, self.tableView (the property) actually reads and writes the value from the tableView instance variable.

一般你声明一个属性,你想让一个实例变量存储这个属性实际的值。例如:self.tableView(属性)实际上它的读写是通过tableView这个实例变量。

In the @interface above, you can see that the author has declared both the property and its backing instance variable. The instance variable sits inside the { } section, the property on its own line below that. (Sometimes you will see the IBOutlet specifier on the instance variable instead of the property. It doesn’t really matter where it goes.)

在上面的声明中,作者同时声明了属性和它支持的实例变量。实例变量声明在{}部分,属性在它之外。(有时会看到IBOutlet这个关键字修饰实例变量而不是属性,这并不重要)

When you do this, you’re essentially writing the same thing twice. Here’s the thing: this hasn’t been necessary for ages! The explicit declaration of the instance variable was only necessary for the iPhone 2.x Simulator because it used an older version of the Objective-C runtime. Quite some time ago now, the Simulator switched to the “modern” runtime, which is also what the actual iPhone uses, and this workaround became unnecessary.

当你这么做的时候,你基本上就是同上的事情写了两次。事情是这样的:这没必要。显示声明实例变量只有在iPhone 2.x模拟器的时候是必要的,因为它使用的是旧版的OC运行时。很久之前,模拟器已经切换到 了“现代的”运行时,那也是iPhone真机在使用的,所以这种解决方案是不必要的。

When you @synthesize a property, the compiler automatically creates that instance variable for you. That’s what @synthesize is for, after all. So there is no need to type the same thing again.

当你synthesize一个属性,编译器会自动创建一个与之相关的实例变量。毕竟那才是synthesize要做的。所以同样的事情没必要再做一次。

Note: In some older versions of Xcode, it used to be that if you allowed the compiler to auto-create instance variables, you couldn’t see the instance variables in the debugger. Happily, this is no longer the case, and you can now see the instance variables in the debugger as expected. So feel free to auto- generate!

注意:在一些老版本的Xcode中,如果你允许编译器自动创建实例变量,你在调试器中看不到这个实例变量。幸运的是,现在已经不是这样,你可以在调试器重看到这个你期望的实例变量。所以,感受自由生成实例变量吧!

So, go ahead and remove these two instance variable declarations from MasterViewController.h, and everything should work as before.

所以,从MasterViewController.h类中删除这两个实例变量,所有的还是会照常工作。

Also go ahead and remove all the instance variable declarations from DetailViewController.h. Same story. Each of those instance variables just exists for the sake of the property with the same name. Get rid of ‘em.

同样将DetailViewController.h类中的实例变量删除。每一个实例变量的存在只是为了跟属性有相同的名字。摆脱他们。

The new, simplified @interface section from DetailViewController.h should look like this:

简化之后的DetailViewController.h类看起来是这样的:

@interface DetailViewController : UIViewController@property (nonatomic, strong) IBOutlet UINavigationBar *navigationBar;@property (nonatomic, strong) IBOutlet UITextField *textField; @property (nonatomic, weak) id <DetailViewControllerDelegate>delegate; @property (nonatomic, copy) NSString * sectionName;@property (nonatomic, assign) NSUInteger indexInSection;@property (nonatomic, copy) NSString *name; @property (nonatomic, copy) NSNumber *value;- (IBAction)cancel:(id)sender; - (IBAction)done:(id)sender;@end

Build and run, and everything should work just as before!

编译运行,所有都是与之前一样的。

But wait, there’s more...

 但是,等等,还有更多。。。

You’re not done with the instance variables just yet. Currently the @interface of MasterViewController.h still declares several instance variables:

你不是完成了实例变量。目前在MasterViewController.h类中仍然声明了几个实例变量:

@interface MasterViewController : UIViewController <. . .> {// Private instance variablesNSDictionary *namesDictionary; NSMutableDictionary *valuesDictionary; NSArray *sortedSectionNames;// For the "sorted by value" screenBOOL sortedByName; NSArray *sortedNames; NSArray *sortedValues;}

As the comment indicates, these are “private” instance variables. They are used internally by this view controller only, and are not supposed to be accessed by any objects outside this class. (Sometimes developers put a @private declaration in there as well.)

评论指出,这些是私有变量。他们只是被这个控制器内部使用,这个类之外的任何对象都不能调用这些实例变量。(有时开发者会用@private修饰)

Wouldn’t it be better if outside objects didn’t know anything about these instance variables at all? In other words, why do they need to be exposed in the @interface section in the header file? There are good historical reasons why that was necessary, once upon a time – Objective-C having been built on top of the C language, to name one – but as of Xcode 4.2, the compiler no longer requires this. It is now possible to place instance variable declarations inside your implementation (.m) files instead.

如果外部对象不知道这些实例变量是不是会更好?换句话说,为什么他们需要在头文件中暴漏在@interface?这是有历史因素的,曾经,OC是建立在C语言上的,Xcode4.2之后,编译器不再需要这个。现在可以在.m文件中声明实例变量。

Cut the instance variable section out of the header file and paste it directly below the @implementation line in MasterViewController.m.

将实例变量的代码剪切到.m文件中
The @interface section in MasterViewController.h should now look like this:
MasterViewController.h文件看起来就是这样:
@interface MasterViewController : UIViewController <. . .>@property (nonatomic, strong) IBOutlet UITableView *tableView; @property (nonatomic, strong) IBOutlet UISegmentedControl *segmentedControl;- (IBAction)sortChanged:(UISegmentedControl *)sender; @end
While the @implementation section in MasterViewController.m should now look like this:
MasterViewController.m文件看起来就是这样:
@implementation MasterViewController {NSDictionary *namesDictionary; NSMutableDictionary *valuesDictionary; NSArray *sortedSectionNames;// For the "sorted by value" screenBOOL sortedByName; NSArray *sortedNames; NSArray *sortedValues;}
That is a lot cleaner! Instance variables are typically only necessary inside the .m file, so that’s where they belong.
这是一个很大的清洁。实例变量只在他们应该在的.m文件中。

Note: You may wonder why some instance variables in this app have properties, and some do not. This is mostly a matter of style – some people like to create properties for everything, and some people don’t like to create properties at all. I only tend to create properties for things that must be accessible from outside a class, and for IBOutlets.

 注意:你可能知道为什么这个应用中一些实例变量需要属性,一些不需要。这主要是一种风格—有的人喜欢把所有的都创建为属性,有的人根本不喜欢创建属性。我只有在外部确实会用到某个对象时才创建为属性或者是IBOutlets。

To synthesize, or not to synthesize

Countless books and tutorials have probably drilled this rule into you: if you have a @property you need to @synthesize it, at least if you want it to be backed by an instance variable. It is also possible to create your own getter and setter methods or to use @dynamic properties, but most of the time you use @synthesize.

大多数的书籍和教程可能告诉你这样一个规则:如果你定义了属性,如果你想得到一个实例变量的支持,你需要用 @synthesize修饰它。你也可以使用 @dynamic方法,自己写setter和getter方法,但实际上你会使用@synthesize。

Well, thanks to the automatic synthesize feature in Xcode 4.5, you don’t have to bother writing @synthesize statements anymore! The compiler will notice your @property statement and automatically synthesize the property and create the backing instance variable for you. Nice, eh?

当然,因为在Xcode4.5出现了新特性,可以不用再使用@synthesize语句了。编译器会自动创建这个属性对应的实例变量以及它的getter和setter方法。

Try this out by removing the @synthesize statements from AppDelegate.m, MasterViewController.m and DetailViewController.m. That shaves about nine or ten lines from the source code. Now build the app.

试试删除AppDelegate.m,MasterViewController.m 和 DetailViewController.m.中的@synthesize句子。那样会删掉九行或十行代码,编译一下项目。

Whoops, the compiler isn’t happy! It gives a number of errors in the code for MasterViewController.m, in this method:

哎呦,编译器不顺利!在MasterViewController.m文件中出现好多错误,在这个方法中:

- (void)viewDidLoad {[super viewDidLoad];if (sortedByName)segmentedControl.selectedSegmentIndex = 0; // error!elsesegmentedControl.selectedSegmentIndex = 1; // error![self updateTableContents];}

The offending lines are the ones that refer to segmentedControl. This used to work before you removed @synthesize, so what’s the big deal?

出现错误的是segmentedControl,在删除@synthesize之前是可以正常运行的,所以现在有什么大不了的?

As it turns out, this is an example of programmer sloppiness. If you declare a property for something, then best practice says you should always refer to it as self.property and not directly through its backing instance variable. Using self.property invokes the proper getter and setter methods, but direct access through the backing instance variable skips those. That may cause issues if your getter or setter does anything special, beyond changing the backing variable.

事实证明,这是一个马虎的程序员的例子。如果你声明了一些东西。使用它的地方最好是self. 而不是使用它的实例。用self. 会调用setter和getter方法,但是使用实例 就会跳过那些!如果你的getter或setter方法做了什么特殊的操作就会引起错误。

It’s best to always write self.property so you don’t have to worry about any of this. Here, however, the programmer forgot to use “self” and just wrote segmentedControl. The fix is to simply add self. to the references to segmentedControl, as follows:

最好使用self. ,这样在这方面你就不会出错。这里,就是程序员忘记使用self 只是写了segmentedControl。更正的方法就是简单的加上self.就可以,如下:

- (void)viewDidLoad {[super viewDidLoad];if (sortedByName) self.segmentedControl.selectedSegmentIndex = 0;elseself.segmentedControl.selectedSegmentIndex = 1;[self updateTableContents]; }}

This still doesn’t answer the question of why this code compiled without problems before you removed @synthesize. That synthesize statement looked like this:

这仍然不能回答这段代码在删除@synthesize之前编译没有问题,synthesize修饰的语句看起来是这样子的:

@synthesize segmentedControl;

 The statement above created a backing instance variable with the same name as the property: in this case, a variable also named segmentedControl. So before, you weren’t actually going through the property (self.segmentedControl) – you were accessing the instance variable directly (segmentedControl). This is an easy mistake to make, since the property and instance variable have the same name.

上面的语句创建了一个和属性同名的实例变量,在这种情况下,变量也名为segmentedControl。所以,你之前实际上不有走属性(self.segmentedControl)—而是你直接访问的实例变量(segmentedControl)。在属性和实例变量名字一样的时候,很容易犯这样的错误。

You may have seen a variation of the synthesize statement that looks like:

你可能会看到变量和 synthesize语句是这样的:

@synthesize segmentedControl = _segmentedControl;

The above notation allows you to specify a different name for the instance variable. This is a good practice, because it makes it harder to make the above mistake – you access the property with self.segmentedControl, and the instance variable with _segmentedControl.

上面的符号是允许你给实例变量指定不一样的名字。这是一个很好的做法,因为这样就不容易犯上面那样的错误—你使用属性通过self.segmentedControl,使用实例变量通过_segmentedControl。

Also, when you do this the compiler helps you out, just like you saw here. Since your program referenced segmentedControl without self, the compiler gave an error because that is neither a valid way to access a property nor the name of an existing variable. It should either be self.segmentedControl or _segmentedControl, not just segmentedControl.

同样,当你这样做的时候,编译器也会帮助你,就像你看到的那样。当编译器引用segmentedControl没有使用self,编译器就会报错,因为这既不是合法的途径访问属性也不是实例变量正确的名字。要使用self.segmentedControl 或 _segmentedControl,而不是segmentedControl。

By renaming the instance variable, you prevent the situation where you’re (mistakenly) using the backing instance variable directly, when you intended to use the property.

通过重命名实例变量,你可以防止当你要使用属性时却错误是使用了实例变量。

And that is exactly what auto-synthesize does: it creates a new backing instance variable named after the property, but prefixed with an underscore, just as if you had typed this:

这就是auto-synthesize做的:它创建了新的实例变量与属性对应,但是实例变量有下划线,格式如下:

@synthesize segmentedControl = _segmentedControl;

To verify this for yourself, change the offending lines to:

验证这些,可以将代码改为:

_segmentedControl.selectedSegmentIndex = . . .;

Now the code should compile successfully. Even though you never declared this variable anywhere yourself, it still exists because of auto-synthesize. (You probably should change it back to use the property before you continue, though.)

现在代码可以编译成功。尽管你在任何地方都没有声明过这个实例变量,它仍然是存在的,因为auto-synthesize。(尽管你可能会将它改回到使用属性之前)

Build and run, and everything should work as usual!

编译运行,所有的都是正常的。

Tip: Your apps don’t need to be iOS 6-only to take advantage of auto- synthesize. Apps compiled with this feature will still work all the way back to iOS 4. Nice!

建议:你的应用不用必须在iOS6下使用auto- synthesize。  应用在iOS4之后都可以编译通过这个特性。

 

备注:这是我实验翻译的一些东西,但是我觉得翻译更多的是学习,所以我决定翻译相对近点出的书吧,不可否认,我翻译的还是比较糟糕的,但是慢慢来吧!

         加油,亲爱的自己!

 

iOS 6 By Tutorials ---第二章--【第二弹】--【翻译】