菜鸟学iPhone开发-Objective-C(转)---中

说明,这是网友翻译的一篇总结Objective‐C语法的文章,我觉得原文写得不错,翻译得也可以,所以直接放到这里给大家看了。全文有点长,所以分成三部分,这是第二部分。

 

完整版中文PDF文件下载可点击 这里

======================================  分隔线  ====================================

内存管理基础

  当你为Mac OSX编写应用程序时,你可以选择允许垃圾回收。这意味着如果不是在特别复杂的情况下,你不用考虑内存管理。然而,你并不会总是工作在支持垃圾回收的环境中。这样的话,你就需要知道一些基本概念。如果你通过手工alloc的方式创建一个对象,之后你需要release这个对象。同样,你也不能手工释放(release)一个能自动释放(autoreleased)的对象,因为这将会使你的应用程序崩溃。

  以下是两个例子:

 

  //string1 将被自动释放

  NSString* string1 = [NSString string];

 

  //必须在用完后手工释放

  NSString* string2 = [[NSString alloc] init];

  [string2 release];

 

  在这里,你可以认为自动释放对象会在当前函数结束的时候被自动释放。

  关于内存管理要学的东西很多,但是我们先了解一下其他的概念,这样我们会有更多的认识。

 

设计类接口

  在Objective-C的语法中,创建一个类是非常简单的。一个类通常分为两部分。类的接口(interface)通常存放在类似ClassName.h的文件中,在这里,我们定义实例变量和公用(public)方法。类的实现存放在ClassName.m这样的文件中,它包含了这些方法的实际实现代码。它通常还定义了客户类不能访问的私有(private)方法。

  一个接口文件看上去像以下这样的。这个类名字叫做Photo,所以接口文件名是Photo.h:

 

  #import <Cocoa/Cocoa.h>

  @interface Photo : NSObject {

    NSString* caption;

    NSString* photographer;

  }

  @end

 

  首先,我们导入了Cocoa.h,目的是将Cocoa应用程序的基本类添加进来。#import指令会自动防止将同一个文件导入多次。@interface表明这是类Photo的声明。冒号后面指定父类(superclass),这里父类是NSObject。在花括号里面声明了两个实例变量:captionphotographer。都是NSString类型,实例变量可以是任何对象类型,包括id类型。最后,@end符号结束类的声明。

 

添加方法

  让我们给实例变量添加一些获取器(getter)

 

  #import <Cocoa/Cocoa.h>

  @interface Photo : NSObject {

    NSString* caption;

    NSString* photographer;

  }

  caption;

  - photographer;

  @end

 

  记住,Objective-C语言中通常省略方法的“get”前缀。方法名字前面的单个减号(-)表明该方法是一个实例方法。如果方法名字前面是一个加号(+),则表明该方法是一个类(static)方法。

 

  编译器会默认一个方法的返回值是一个id类型的对象,所有的输入参数也默认是id; 类型。上述代码在技术上是正确的,但是我们一般不这样写,我们需要给这些方法指定返回值类型。

 

  #import <Cocoa/Cocoa.h>

    @interface Photo : NSObject {

    NSString* caption;

    NSString* photographer;

  }

  - (NSString*)caption;

  - (NSString*)photographer;

  @end

 

  现在,我们来添加设置器(setter):

 

  #import <Cocoa/Cocoa.h>

  @interface Photo : NSObject {

    NSString* caption;

    NSString* photographer;

  }

  - (NSString*)caption;

  - (NSString*)photographer;

  - (void) setCaption: (NSString*)input;

  - (void) setPhotographer: (NSString*)input;

  @end

 

  设置器不需要有返回值,所以我们指定返回值是void

 

类实现

  现在,我们从获取器(getter)开始,来创建一个类的实现。

 

  #import "Photo.h"

  @implementation Photo

  - (NSString*) caption {

    return caption;

    }

  - (NSString*) photographer {

  return photographer;

  }

  @end

 

  这段代码以@implementation和类的名字开始,并且像接口一样,有一个@end。所有的方法必须写在这两条语句之间。如果你写过代码,就会觉得上面的获取器看上去很熟悉,所以我们还是来看一看设置器,它们需要多一点解释。

  - (void) setCaption: (NSString*)input

  {

    [caption autorelease];

    caption = [input retain];

  }

  - (void) setPhotographer: (NSString*)input

  {

    [photographer autorelease];

    photographer = [input retain];

  }

 

  每一个设置器都要处理两个变量,第一个是当前引用的对象,第二个是新输入的对象。在带有垃圾回收机制的环境中,我们可以直接设置成新的值。

 

  - (void) setCaption: (NSString*)input

  {

    caption = input;

  }

 

  但是,如果你不能使用垃圾回收,你需要release旧的对象,并且retain新的对象。释放一个对象的引用实际上有两种方法:release autorelease。标准的release会立刻释放对象的引用。autorelease会等一会儿才释放,但是引用实际上会一直存在,直到当前方法结束(除非你添加自定义的代码来明确的改变它)。在设置器里面使用autorelease方法会更加安全一些,因为要改变的变量的新旧两个值可能指向的是同一个对象。而你可能不希望立刻释放实际上你要保留的对象。现在,这看上去有点让人迷惑,但是随着你的不断学习,你就会有更多的认识。所以,现在不必彻底的理解这些。

 

Init

  我们可以创建一个init方法用来给我们的实例变量设置初始化值:

 

  - (id) init

  {

    if ( self = [super init] )

    {

      [self setCaption:@"Default Caption"];

      [self setPhotographer:@"Default Photographer"];

    }

    return self;

  }

 

  这段代码是完全不需要加以说明的,尽管第二行看上去有点不常见。它是一个单个的等号(=),作用是将[super init]的结果赋值给self。这实际上是要求父类做(父类的)初始化操作。if语句的作用是在尝试设置(本对象的)缺省值之前验证父类是否初始化成功。

 

Dealloc

  dealloc方法在一个对象从内存中删除时被调用。通常在这个方法里面释放所有对象里的实例变量。

 

  ‐ (void) dealloc

  {

    [caption release];

    [photographer release];

    [superdealloc];

  }  

 

  在前两行,我们直接调用了实例变量的release方法。在这里,我们不需要使用autorelease,因为标准的release更快一些。最后一行非常重要,我们发送了一个[superdealloc]消息,要求父类做清理工作。如果我们不做的话,该对象就不会被从内存中删除,这就造成了内存泄露。当启用垃圾回收机制时,对象的dealloc方法不会被调用。此时,你可以实现一个finalize方法来代替它。

 

内存管理

  Objective-C的内存管理是基于引用计数的。你要做的事情只是关注你的引用,而释放内存的工作实际上由运行环境完成。在最简单的情形中,你分配的(alloc)对象,或者是保留(retain)在一些地方的对象,都需要给他们发送一个release消息。这也意味着,如果你使用了一次alloc,然后又retain了一次,那么你需要release两次才能释放该对象的内存。

 

  这就是引用计数的理论。在实际应用中,通常只有两个原因我们才会创建一个对象:

  1. 作为一个实例变量保留。

  2. 在函数内部作为临时变量使用。

  大多数情况下,一个实例变量的设置器(setter)会自动释放(autorelease)原来引用的对象,同时保留(retain)新的。你只需要保证在dealloc函数中释放(release)了它就行了。

  那么,我们实际要做的工作就只有管理函数内部的本地引用了。在这里只有一条规则:如果过你通过alloc或者copy创建了一个对象,在函数结尾的地方给它发送一个release或者autorelease消息就行了。如果你是通过其它方式创建的对象,就什么也别做。下面是第一个例子,管理实例变量:

 

  - (void) setTotalAmount: (NSNumber*)input

  {

    [totalAmount autorelease];

    totalAmount = [input retain];

  }

  - (void) dealloc

  {

    [totalAmount release];

    [super dealloc];

  }

 

  下面是另外一个例子,关于本地引用。我们只需要释放通过alloc创建的对象就行了:

 

  NSNumber* value1 = [[NSNumber alloc] initWithFloat:8.75];

  NSNumber* value2 = [NSNumber numberWithFloat:14.78];

  // only release value1, not value2

  [value1 release];

 

  下面是一个组合例子,将一个本地引用设置给实例变量:

 

  NSNumber* value1 = [[NSNumber alloc] initWithFloat:8.75];

  [self setTotal:value1];

  NSNumber* value2 = [NSNumber numberWithFloat:14.78];

  [self setTotal:value2];

  [value1 release];

 

  注意,不论你是不是把本地引用当成实例变量一样赋值,管理它们都是完全相同的。你不必考虑设置器(setter)是如何实现的。如果你理解了这些,你就理解了关于Objective-C内存管理中90%你需要知道的内容。

posted @ 2010-09-27 13:30  鬼手如冰  阅读(574)  评论(0编辑  收藏  举报