(译)Learn Objective-C

原文链接:http://cocoadevcentral.com/d/learn_objectivec/

开始自学IOS开发,找了一篇比较经典的Objective-C微教程,只是读一遍终觉印象不深,索性尝试翻译一下

再附上CSDN上的翻译:http://mobile.51cto.com/hot-232833.htm

Learn Objective-C

1,Calling Methods

让我们从简单的示例开始,调用一个对象的方法,基本语法是这样的:

[object method];
[object methodWithInput:input];

对象的方法可以返回值:

output = [object methodWithOutput];
output = [object methodWithInputAndOutput:input];

你也可以调用类中的如何创建对象的方法,在下面的例子中,我们调用NSString类的string方法,从而返回一个新的NSString对象.

id myObject = [NSString string];

id类型意味着myObject变量可以被赋予任意类型的对象,所以在你编译app时,并不知道它真实实现的类和方法.

在下面的例子里,它很明显将是一个NSString,所以我们在前面指定它的类型:

NSString* myString = [NSstring string];

这是一个NSString变量,所以如果我们尝试在这个对象上使用一个NSString不支持的方法,编译器就会发出警告.

注意类类型的右边有个*号,所有的Objective-C对象变量都是指针类型.id类型是预定义的指针类型,所以不需要加*号.

Nested Messages

在许多语言中,嵌套调用方法和函数是这样的写法: 

function1( function2() );

function2的返回值作为function1的传入参数.在Objective-C中,嵌套信息这样写:

[NSString stringWithFormat:[prefs format]];

应避免在同一行里调用超过两个嵌套信息,这样会导致可读性变差.

Mulit-Input Methods

通常我们会需要一个方法拥有多个参数.在Objective-C中,一个方法名能被分成N段.在头文件里,一个多参方法定义如下:

-(BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile;

调用方法:

BOOL result = [myData writeToFile:@"/tmp/log.txt" atomically:NO];

这里不仅仅是命名参数.这个方法在系统运行中的名字实际是writeToFile:atomically:

2,Accessors

所有的实例变量在Objective-C中默认是私有的,所以在多数情况下你需要用Accessors 来 获取/设置 变量的值.
有两个语法,下面是传统的1.X语法:

[photo setCaption:@"Day at the Beach"];
output = [photo caption];

第二行代码不是直接读取实例变量.实际上它是调用了caption方法.在多数情况下,Objective-C中的getters不加前缀get.

无论何时你只要看到方括号里的代码,这就代表给类或对象发送消息.

Dot Syntax

Dot Syntax是Objective-C 2.0中getters和setters的新方法,这也是Mac OS X 10.5的一部分:

photo.caption = @"Day at the Beach";
output = photo.caption;

这两种方式你都可以使用,但在一个工程最好选择统一的风格,

注意:Dot Syntax只能用在setters和getters,不能用于其它用途的方法.



3,Creating Objects


有两个创建类的方法,第一种我们之前已经写过:

NSString* myString = [NSString string];

这是更加便捷自动的方式,在这个例子中,你创建了一个autoreleased类型的类,关于autorelease后面我们会涉及到更多细节.

在多数情况下,我们需要用手动的方式来创建类:

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

这是一个嵌套方法的调用.首先调用了NSString自己的alloc方法.请求驻留内存和实例化一个对象,这些属于相当底层的调用.

而后面则是在新创建的对象上调用init方法.init实现通常用作一些基本设置,比如创建实例变量.
我们并不知道它的具体细节,它是作为一个客户端的类实现的.

有时候,你要用到需要赋值的init

NSNumber* value = [[NSNumber alloc] initWithFloat:1.0];

 

4,Basic Memory Management


如果在Max OS X上写应用,你可以选择启用垃圾回收的功能.一般来说,这意味着除非遇到复杂的情况,你不需要考虑内存管理的问题.
尽管如此,你不会总在一个支持垃圾回收的环境上工作.所以你需要稍微知道一些基本的内存管理概念.

如果你通过手工alloc的方式创建了一个对象,你需要在后面release掉这个对象.
你不应该手动release一个autoreleased类型的对象,因为如果你这样做,程序就会崩溃.

这里有两个例子:

//string1 will be released automatically
NSString* string1 = [NSString astring];

//must release this when done
NSString* string2 = [[NSString alloc] init];
[string2 release];

在这个示例中,你可以假设自动释放的对象(string1)会在当前函数调用结束后销毁.

关于内存管理还有更多的学习,但我们会在后面了解更多的概念后再了解它.

 

5,Designing a Class Interface

Objective-C创建类的语句非常简单,通常含有两个部分:

类的接口一般保存在ClassName.h文件中,定义了实例变量和公共方法.

实现部分则在ClassName.m文件中,这里面包含了具体实现方法的代码,同样也会经常定义一些子类不可见的private方法.

这里有个接口文件,类名叫Photo,所以文件名是Photo.h:

#import <Cocoa/Cocoa.h>

@interface Photo: NSObject {
NSString* caption;
NSString* photographer;
}
@end

首先,我们导入Cocoa.h,这个是Cocoa程序所有基本的类.#import指令会自动避免在同一个文件中指定多次.

@interface是指Photo类的一个声明,冒号后面跟着NSObject这个超类.
在大括号里面,有两个实体变量:caption和photographer.都是NSStrings类型,也可以是任意对象类型,包括id类型.
最后,@end结束类的声明.

 

Add Methods

让我们对实体变量加一些getters

#import <Cocoa/Cocoa.h>

@interface Photo: NSObject {
NSString* caption;
NSString* photographer;
}

- caption;
- photographer;
@end

记住,Objective-C的特点是省略get前缀.

方法名前面加 - 表示是实例方法
方法名前面加 + 表示是类方法.

默认情况下,编译器会假设方法返回id类型的对象,所有传入参数也默认是id类型.
所以上面的代码技术上是没有问题的,但通常不会这样做.
让我们给返回值添加上指定的类型:

#import <Cocoa/Cocoa.h>

@interface Photo: NSObject {
NSString* caption;
NSString* photographer;
}

- (NSString*) caption;
- (NSString*) photographer;
@end

现在再添加setters:

#import <Cocoa/Cocoa.h>

@interface Photo: NSObject {
NSString* caption;
NSString* photographer;
}

- (NSString*) caption;
- (NSString*) photographer;

- (void) setCaption: (NSString*)input;
- (void) setPhotographer: (NSString*)input;
@end

Setters是不需要返回值的,所以我们指定类型为void

6, Class Implementation

然后我们开始创建实现部分,从getters开始:

#import "Photo.h"

@implementation Photo

- (NSString*) caption {
return caption;
}
-(NSString*) photographer {
return photographer;
}

@end

实现部分开始于 @implementation 和 类名,最后@end.看起来有点像类的接口声明部分.

在接口声明的方法,同时要在实现部分实现.所有的方法都是如此.

如果你有过编程基础,会对getters看起来非常熟悉,所以我们转移重点,更多的来解释说明setters:

- (void) setCaption: (NSString*)input
{
[caption autorelease];
caption = [input retain];
}

- (void) setPhotographer: (NSString*)input
{
[photographer autorealease];
photographer = [input retain];
}

每个setters都分配了两个变量,第一个是已经存在的对象的引用,第二个是新传入的对象.在垃圾回收环境中,

我们可以只直接设置新对象:

- (void) setCaption: (NSString*)input
{
caption = input;
}

但如果你没有用到垃圾回收,你就需要release旧对象,然后retain新对象.

实际中,有两种方式释放一个对象的引用:release和autorelease.release会立即移除引用.autorelease则会在之后的某个时间进行release,在此之前,对象会一直驻留在内存中直到函数结束.(除非添加自定义代码来处理).

atuorelease包含在setters里面更安全,因为变量的新值和旧值不能指向同一个对象.你不会立即释放你想要保留的那个对象.
这样听起来有点迷惑,没关系,后面随着进度你会理解的更加深入,现在你不需要完全明白.

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];
[super dealloc];
}

最开始的两行,我们给每个实体变量发送release命令,这里不需要使用autorelease,标准的release更迅速.

最后一行非常重要,我们必须发送super dealloc信息来请求超类来做自我清除.如果我们没有这样做,对象将不会移除,这样会造成内存泄露.

当垃圾回收功能启用时,不会调用dealloc方法,声明finalize方法来代替.

7,More on Memory Management

Objective-C内存管理系统称作引用计数,你所要必须做的就是保持对引用的跟踪,并在运行中正确的在内存中释放.
简单来讲:你alloc一个对象,作为一些指针retain,那就需要为每个alloc/retain发送release.所以如果你使用了一次alloc,一次retain,就需要release两次.

这就是引用计数的理论,在实践中,通常只有两个原因创建对象:
1,将它作为一个实体变量保留
2,在函数中单一的临时使用

在多数情况下,一个实体变量的setters,应该只是autorelease旧对象,并retain新对象.然后一定要dealloc中释放它们.

所以唯一的真正的工作就是管理函数内的本地引用.一个原则:如果你使用alloc或copy创建了一个对象,就要在功能结束时release或者autorelease它们.如果用其它方式创建的对象,就什么也不要做.

下面是第一种情况,管理一个实体变量

- (void) setTotalamount: (NSNumber*)input
{
[totalAmount autorelease];
totalAmount = [input retain];
}

- (void) dealloc
{
[totalAmount release];
[super dealloc];
}

然后另外一种情况,本地引用,我们只需要release用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];

注意不管你是否将它们赋值给实体变量,管理本地引用的规则都是一致的.你不需要考虑setters是如何实现的

如果你弄懂这些,你就弄懂了Objective-C内存管理的90%

8,Logging

输出日志信息到控制台在Objective-C中非常简单.事实上,NSLog()函数几乎和C的printf()完全相同,除了需要添加%@标示来表示对象.

NSLog ( @"The current date and time is : %@",[NSDate date] );

你能记录一个对象到控制台,NSLog函数调用对象的Description方法,并以NSString的形式打印出来.你能在你的类里重载description方法,返回自定义的字符串.

 

9,Properties

properties作为OC里的一个功能,允许我们自动产生accessors,另外还有一些别的好处.
我们让Photo类来使用properties.
下面是我们之前看过的代码:

#import <Cocoa/Cocoa.h>

@interface Photo : NSObject {
NSString* caption;
NSString* photographer;
}
- (NSString*) caption;
- (NSString*) photographer;

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

@end

一旦用属性来实现便如下:

#import <cocoa/cocoa.h>

@interface Photo : NSObject {
NSString* caption;
NSString* photographer;
}
@property (retain) NSString* caption;
@property (retain) NSString* photographer;

@end

@property在OC里是声明属性的指令.圆括号里的retain指定setter保留输入值,接下来的部分指定属性的名称和类型.

现在我们看一下类的实现部分:

#import "Photo.h"

@implementation Photo

@synthesize caption;
@synthesize photographer;

- (void) dealloc
{
[caption release];
[photographer release];
[super dealloc];
}

@end

@synthesize指令会自动生成setters和getters,所以我们只需要在类里实现dealloc方法就可以了.

Accessors只会在setters和getters不存在的时候自动创建它们,所以随意的用@synthesize来指定属性,如果需要可以在随后的实现部分自定义setter或getter.编译器会检查哪个方法没有,自动填充漏掉的方法.

还有好多关于属性声明的选项,不过那些都在本教程范围之外了.

10,Calling Methods on Nil

在OC中,nil对象类似于其它语言的null.不同的是你可以调用nil方法,而不会引起程序崩溃或抛出一个异常.

这个技巧在框架中有许多不同方面的使用方式,但目前对你来说最主要的是当你调用一个方法时不必检查它是否为nil.
如果你调用了一个nil的方法来返回一个对象,就会得到一个nil.

我们可以利用这个特性稍微改善下dealloc方法:

- (void) dealloc
{
self.caption = nil;
self.photographer = nil;
[super dealloc];
}

我们把nil赋给一个实体变量,setter只会保留nil(没有做任何事情)和release旧的值.

这样做的好处是对象的变量指针不会指向到其它一些随机数据上.

注意我们在这里使用了 self.<var>语法,意思是使用setter并使用内存管理释放.如果直接设置nil,会引起内存泄露.

//incorrect. causes a memory leak.
// use self.caption to go through setter
caption = nil;

11,Categories

Categories是OC中最有用的功能之一.本质上,一个category允许你在一个已存在的类上添加方法,而不必用子类继承,也不需要知道类内部具体实现的细节.

这个特别有用,你能在内嵌的对象上添加方法.如果你想在你的应用中所有的NSString实例添加一个方法,你只需要添加一个category.而不必每个都使用定制子类.

举个例子,如果我想在NSString上添加一个方法,确定其内容是否是一个URL:

#import <cocoa/cocoa.h>

@interface NSString (Utilities)
- (BOOL) isURL;
@end

这个和类声明非常相似,不同的是这里没有列出超类(super class),括号里的是category的名字,这个名字可以任意取,通过名字和内部的方法沟通.

下面是实现部分,记住这个不是一个好的检测URL的实现,我们只是通过这个来简单诠释category的概念:

#import "NSString-Utilities.h"

@implementation NSString (Utilities)

- (BOOL) isURL
{
if ( [self hasPreFix:@"http://"] )
return YES;
else
return NO;
}

@end

接下来你就能在所有的NNString上使用这个方法.下面的代码将会在命令行中打印出"string1 is a URL":

NSString* string1 = @"http://pixar.com/";
NSString* string2 = @"Pixar";

if ( [string1 isURL] )
NSLog (@"string1 is a URL");

if ( [string2 isURL] )
NSLog (@"string2 is a URL");

不像子类,categories不能添加到实体变量上.虽然能通过categories来重载类中已存在的方法,但这样做必须非常小心.

记住,当你通过category来改变一个类时,它会影响整个应用程序中该类的所有对象.

 

 

原文地址:https://www.cnblogs.com/alphaqcode/p/3910101.html