OC基础教程之基本语法

第一节总括

这一节是对Objective-C(以后简称OC)的简要介绍,目的是使读者对OC有一个概括的认识。

1.面象的读者

在阅读本文之前,应具备使用与C类似的编程语言(如C,C++,JAVA)的一些经验,同时熟悉面向对象编程。

2.OC简介

OC是以SmallTalk为基础,建立在C语言之上,是C语言的超集。20世纪80年代早期由 Brad J.Cox设计,2007年苹果公司发布了OC 2.0,并在iPhone上使用OC进行开发。

3.OC学习内容

学习的内容主要包括语法和Cocoa框架两部分。本文主要对语法进行介绍。

4.IDE

编写OC程序最主要的编译环境是Xcode,它是苹果官方提供的IDE,官网中的SDK包括Xcode,可以通过下载SDK来获得它。但是Xcode只支持MacOSX,所以如果要在其它环境下编写OC程序,要使用其它IDE。Linux/FreeBSD用GNUStep,Windows NT5.x(2000,XP)要先安装cywin或mingw,然后安装GNUStep。同时仅仅通过文本编辑器,GCC的make工具也可以用于开发。

注:如果要使用到Cocoa的话,只能在Apple公司的Xcode上。

5.框架

OC编程中主要用到的框架是Cocoa,它是MacOS X中五大API之一,它由两个不同的框架组成FoundationKit 和ApplicationKit。Foundation框架拥有100多个类,其中有很多有用的、面向数据的低级类和数据类型,如NSString,NSArray,NSEnumerator和NSNumber。ApplicationKit包含了所有的用户接口对象和高级类。这些框架本文不做重点介绍,如果要深入了解可以去看Xcode自带的文档。

6.特别之处

初次接触OC时,会发现许多和其它语言不同的地方,会看到很多的+,-,[ ,] ,@,NS等符号,这些符号在以后的编程中将经常看到,这部分内容在第二节中介绍。先熟悉一下OC的代码:

#import            "ClassA.h"

#import

int            main( int argc, const char *argv[] ) {

ClassA            *c1 = [[ClassA alloc] init];

ClassA            *c2 = [[ClassA alloc] init];

//            print count

printf(            "ClassA count: %i\n", [ClassA initCount] );

ClassA            *c3 = [[ClassA alloc] init];

//            print count again

printf(            "ClassA count: %i\n", [ClassA initCount] );

[c1            release];

[c2            release];

[c3            release];

return            0;

}

除了这些语言要素上的不同,OC也提供了一些很好的特性,如类别,扮演(Posing)等,这些在运行时的特性使得编程更加灵活。

7.优缺点

每一个语言都有其优缺点,OC也不例外,这就要求在选择语言时权衡利弊。对于OC,只要善于利用它的优点,你会发现它是一个简单,灵活,高效的语言。以下列举了它的一些特点:

优点:类别、扮演(Posing)、动态类型、指针计算、弹性信息传递、不是一个过度复杂的c衍生语言、可通过Objective-c++与c++结合

缺点:没有命名空间、没有操作符重载、不像c++那样复杂

第二节:对C的扩展

1.扩展名

OC是ANSI版本C的一个超集,它支持相同的C语言基本语法。与C一样,文件分为头文件和源文件,扩展名分别为.h和.m。如果要加入c++的语法,需要用到.mm,这里不做介绍。

.h

头文件。头文件包涵类的定义、类型、方法以及常量的声明

.m

源文件。这个典型的扩展名用来定义源文件,可以同时包含C和Objective-C的代码。

2.#import

在OC里,包含头文件有比#include更好的方法#import。它的使用和#include相同,并且可以保证你的程序只包含相同的头文件一次。相当于#include+ #pragma once的组合。

例如要包含Foundation框架中的Foundation.h文件,可以像下面这样。

#import

注:每个框架有一个主的头文件,只要包含了这个文件,框架中的所有特性都可以被使用。

[email protected]符号

@符号是OC在C基础上新加的特性之一。常见到的形式有@”字符串”,%@ , @interface,@implement等。@”字符串”表示引用的字符串应该作为Cocoa的NSString元素来处理。@interface等则是对于C的扩展,是OC面向对象特性的体现。

注:这里提一个小技巧,只要看到@符号,就可以认为它是对于C的一个扩展。

4.NSLog()

在OC中用的打印函数是NSLog(),因为OC是加了一点”特殊语料”的C语言,所以也可以用printf()但是NSLog()提供了一些特性,如时间戳,日期戳和自动加换行符等,用起来更方便,所以推荐使用NSLog()。下面是两种输出的对比。

使用NSLog()输出任意对象的值时,都会使用%@格式说明。在使用这个说明符时,对象通过一个名为description的方法提供自己的NSLog()格式。

下面分别是使用NSLog()和使用printf()的相应输出:

2010-10-15 14:54:21。426            10_15[1973:207] Hello World!

Hello            World!

注:NS前缀告诉你函数来自Cocoa而不是其他工具包。

5.BOOL

BOOL是OC中的布尔类型,它和C中的bool有如下区别

BOOL

YES(1),NO(0)

bool

true(!0),false(0)

6.id

这是OC新加的一个数据类型,它是一般的对象类型,能够存储任何类型的方法。

7.nil

在OC中,相对于C中的NULL,用的是nil。这两者是等价的。下面是nil的定义。

#define            nil NULL

第三节:创建对象

1.接口和实现

在OC中定义一个类需要有两个部分:接口和实现。接口文件包含了类的声明,定义了实例变量和方法。实现文件包含了具体的函数的实现代码。下图显示了一个叫 MyClass的类,它继承自NSObject基类。类的定义总是从@interface开始到@end结束。在类名后面的是父类的名称。实例变量被定义 在两个花括号之间。在实例变量下面的是方法的定义。一个分号用来结束一个变量或者方法。

下面的代码显示了MyClass这个类的实现代码。就像类的定义规则一样,类实现文件也被两个标识框起来,一个是 @implementation,还有一个是@end。这两个指令标识符告诉编译器程序从哪里开始编译到哪里结束。类中的方法名称的定义和它接口文件中的 定义是一样的,除了实现文件中有具体的代码以外。

@implementation            MyClass

-            (id)initWithString:(NSString *) aName

{

if            (self = [super init]) {

count            count = 0;

data            = nil;

name            = [aName copy];

return            self;

}

}

+            (MyClass *)createMyClassWithString: (NSString *) aName

{

return            [[[self alloc] initWithString:aName] autorelease];

}

@end

当你要把一个对象保存进变量,要使用指针类型。OC同时支持强和弱变量对象。强类型对象在变量类型定义的时候包含了类名。弱对象使用id类型作为实例变量。下面的例子同时显示了定义MyClass中的强弱两种类型的变量

MyClass*             myObject1;    // Strong typing

id                   myObject2;    // Weak typing

2.方法

一个方法定义包含了方法类型,返回类型,一个或者多个关键词,参数类型和参数名。在OC中一个类中的方法有两种类型:实例方法,类方法。实例方法前用(-)号表明,类方法用(+)表明,通过下图可以看到,前面有一个(-)号,说明这是一个实例方法。

在OC中,调用一个方法相当于传递一个消息,这里的消息指的是方法名和参数。所有的消息的分派都是动态的,这个体现了OC的多态性。消息调用的方式是使用方括号。如下面的例子中,向myArray对象发送insertObject:atIndex:这个消息。

[myArray            insertObject:anObj atIndex:0];

这种消息传递允许嵌套

[[myAppObject            getArray] insertObject:[myAppObject getObjectToInsert] atIndex:0];

前面的例子都是把消息传递给实例变量,你也可以把消息传递给类本身。这时要用类方法来替代实例方法 。你可以 把他想象成静态C++类(当然不完全相同)。

类方法的定义只有一个不一样那就是用加号(+)代替减号(-)。下面就是使用一个类方法。

NSMutableArray*              myArray = nil;    // nil is essentially the same as NULL

//            Create a new array and assign it to the myArray variable.

myArray =            [NSMutableArray arrayWithCapacity:0];

3.属性

属性提供了比方法更方便的访问方式。通过property标识符来替代getter和setter方法。使用方法就是在类接口文件中用@property标识符,后面跟着变量的属性,包括 copy, tetain, assign ,readonly , readwrite,nonatomic,然后是变量名。同时在实现文件中用@synthesize标识符来取代getter和setter方法。

@property            BOOL flag;

@property            (copy) NSString* nameObject;

//            Copy the object during assignment.

@property            (readonly) UIView* rootView;  // Create only a getter method

接口文件中使用@property

@synthesize            flag,nameObject,rootView;

实现文件中使用@synthesize

属性的另一个好处就是,可以使用点(.)语法来访问,如下所示:

myObject.flag            = YES;

CGRect              viewFrame = myObject.rootView.frame;

第四节:继承

继承的语法如下,冒号后的标识符是需要继承的类。

@interface            Circle : NSObject

1.不支持多继承

要注意的是OC只支持单继承,如果要实现多继承的话,可以通过类别和协议的方式来实现,这两种方法将在后面进行介绍。

2.Super关键字

OC提供某种方式来重写方法,并且仍然调用超类的实现方式。当需要超类实现自身的功能,同时在前面或后面执行某些额外的工作时,这种机制非常有用。为了调用继承方法的实现,需要使用super作为方法调用的目标。下面是代码示例:

@implementation            Circle

-(void)setFillColor:            (ShapeColor) c

{

if            (c== kRedColor){

c            = kGreenColor;

}

[super            setFillColor: c];

}

@end

Super来自哪里呢?它既不是参数也不是实例变量,而是由OC编译器提供的某种神奇功能。向super发送消息时,实际上是在请求OC向该类的超类发送消息。如果超类中没在定义该消息,OC将按照通常的方式在继承链中继续查找对应的消息。

第五节:对象初始化

1.分配与初始化

对象的初始化有两种方法:一种是[类名new], 第二种是[[类名 alloc]init]。这两种方法是等价的,不过,通常的Cocoa惯例是使用alloc和init,而不使用new.一般情况下,Cocoa程序员只是在他们不具备足够的水平来熟练使用alloc和init方法时,才将new作为辅助方法使用。

[[类名alloc]init]有两个动作。alloc是分配动作,是从操作系统获得一块内存并将其指定为存放对象的实例变量的位置。同时,alloc方法还将这块内存区域全部初始化为0。与分配动作对应的是初始化。有如下两种初始化写法。

Car *car =            [[Class alloc] init];

写法1

Car            *car = [Car alloc];

[car            init];

写法2

应该使用第一种写法,因为init返回的对象可能不是以前的那个。

2.编写初始化方法

下面是一段初始化的代码

-(id)init

{

if(self = [super            init]){

engine = [Engine            new];

}

}

使用self= [super init]的作用是使超类完成它们自己的初始化工作。同时因为init可能返回的是不同的对象,实例变量所在的内存位置到隐藏的self参数之间的跳离又是固定的,所以要这样使用。

注:这部分可以参考书[1]144页。

第六节:协议

这里的协议是正式协议,相对的还有非正式协议,这在类别一节中有介绍。正式协议是一个命名的方法列表。它要求显式地采用协议。采用协议意味着要实现协议的所有方法。否则,编译器会通过生成警告来提醒你。

1.声明协议

@protocol            NSCopying

-(id)            copyWithZone:(NSZone            *)zone;

@end

2.采用协议

@interface Car :            NSObject

{

// instance            variables

}

@end

协议可以采用多个,并且可以按任意顺序列出这些协议,没有什么影响。

3.OC 2.0的新特性

OC2.0增加了两个新的协议修饰符:@optional和@required,因此你可以像下面这样编写代码:

@protocol            BaseballPlayer

-(void)drawHugeSalary;

@optional

-(void)slideHome;

-(void)catchBall;

@required

-(void)swingBat;

@end

因此,一个采用BaseballPlayer协议的类有两个要求实现的方法:-drawHugeSalary和-swingBat,还有3个不可选择实现的方法:slideHome,catchBall和throwBall。

第七节:委托

Cocoa中的类经常使用一种名为委托(delegate)的技术,委托是一种对象,另一个类的对象会要求委托对象执行它的某些操作。常用的是,编写委托 对象并将其提供给其他一些对象,通常是提供给Cocoa生成的对象。通过实现特定的方法,你可以控制Cocoa中的对象的行为。

通过下面的例子,可以更清楚地理解委托的实现原理。其中A对象需要把一些方法委托给其它对象来实现,例子中就是对象B,B实现了含A对象特定方法的协议ADelegate,从而可以在B中实现A委托的方法。

@protocol            ADelegate

-            (void)aDelegateMethod;

……

@end

@interface            A : NSObject {

……

id            delegate;

}

@property            (readwrite, assign)

id            delegate;

……

@end

@implementation            A

@synthesize            delegate;

-            (void)aMethod{

[delegate            aDelegateMethod];

……

}

@end

A类

@interface            B : NSObject

@end

@implementation            B

-            (id)init {

……

[[A            sharedA] setDelegate:self];

}

-            (void)aDelegateMethod{   //B中实现A委托的方法

……

}

@end

B类

注:实现委托还可以使用类别,在第八节中将做介绍

第八节: 类别

类别允许你在现有的类中加入新功能,这些类可以是框架中的类,并且不需要扩充它。

1.声明类别

@interface            NSString (NumberConvenience)

-(NSNumber *)            lengthAsNumber;

@end

该声明表示,类别的名称是NumberConvenience,而且该类别将向NSString类中添加方法。

2.实现类别

@implementation            NSString (NumberConvenience)

-(NSNumber            *) lengthAsNumber

{

unsigned int            length = [self length];

return            ([NSNumber numberWithUnsignedInt: length]);

}

@end

3.局限性

类别有两方面的局限性。第一,无法向类中添加新的实例变量。类别没有位置容纳实例变量。第二,名称冲突,即类别中的方法与现有的方法重名。当发生名称冲突时,类别具有更高的优先级。这点可以通过增加一个前缀的方法解决。

4.非正式协议和委托类别

实现委托除了第七节中应用协议的方式,还可以使用类别。具体做法就是把委托对象要实现的方法声明为一个NSObject的类别。如下面的代码所示:

@interface            NSObject(NSSomeDelegateMethods)

-(void)someMethod;

@end

通过将这些方法声明为NSObject的类别,使得只要对象实现了委托方法,任何类的对象都可以成为委托对象。创建一个NSObject的类别称为“创建 一个非正式协议”。非正式协议只是一种表达方式,它表示“这里有一些你可能想实现的方法”,第六节介绍的协议可以叫做正式协议。

非正式协议的作用类似于使用许多@optional的正式协议,并且前者正逐渐被后者所代替。

5.选择器

选择器只是一个方法名称,它以OC运行时使用的特殊方式编码,以快速执行查询。你可以使用@selector()预编译指令指定选择器,其中方法名位于圆括号中。如一个类中setEngine:方法的选择器是:@selector(setEngine:)。

因为选择器可以被传递,可以作为方法的参数使用,甚至可以作为实例变量存储。这样可以生成一些非常强大和灵活的构造。

第九节:Posing

Posing有点像类别,但不太一样。它允许你扩充一个类,并且全面性地扮演(pose)这个超类。例如:你有一个扩充NSArry的 NSArrayChild对象。如果你让NSArrayChild扮演NSArry,则在你的代码中所有的NSArray都会自动被替代为 NSArrayChild.

@interface            FractionB: Fraction

-(void)            print;

@end

@implementation            FractionB

-(void)            print {

printf(            "(%i/%i)", numerator, denominator );

}

@end

Fraction.m

int            main( int argc, const char *argv[] ) {

Fraction            *frac = [[Fraction alloc] initWithNumerator: 3 denominator: 10];

//            print it

printf(            "The fraction is: " );

[frac            print];

printf(            "\n" );

//            make FractionB pose as Fraction

[FractionB            poseAsClass: [Fraction class]];

Fraction            *frac2 = [[Fraction alloc] initWithNumerator: 3 denominator: 10];

//            print it

printf(            "The fraction is: " );

[frac2            print];

printf(            "\n" );

//            free memory

[frac            release];

[frac2            release];

return            0;

}

Main.m

The            fraction is: 3/10

The            fraction is: (3/10)

输出

这个程序的输出中,第一个fraction会输出3/10,而第二个会输出(3/10),这是FractionB中实现的方式。poseAsClass这个方法是NSObject的一部分,它允许子类扮演超类。

第十节:动态识别 (Dynamictypes)

下面是应用动态识别时所用到的方法:

-(BOOL)isKindOfClass:            classObj

是否是其子孙或一员

-(BOOL)isMemberOfClass:            classObj

是否是其一员

-(BOOL)respondsToSelector:            selector

是否有这种方法

+(BOOL)instancesRespondToSelector:            selector

类的对象是否有这种方法

-(id)performSelector:            selector

执行对象的方法

通过下面的代码可以更清楚地理解动态类型的使用:

import            "Square.h"

#import            "Rectangle.h"

#import

“     int            main( int argc, const char *argv[] ) {

Rectangle            *rec = [[Rectangle alloc] initWithWidth: 10 height: 20];

Square            *sq = [[Square alloc] initWithSize: 15];

//            isMemberOfClass

//            true

if            ( [sq isMemberOfClass: [Square class]] == YES ) {

printf(            "square is a member of square class\n" );

}

//            false

if            ( [sq isMemberOfClass: [Rectangle class]] == YES ) {

printf(            "square is a member of rectangle class\n" );

}

//            false

if            ( [sq isMemberOfClass: [NSObject class]] == YES ) {

printf(            "square is a member of object class\n" );

}

//            isKindOfClass

//            true

if            ( [sq isKindOfClass: [Square class]] == YES ) {

printf(            "square is a kind of square class\n" );

}

//            true

if            ( [sq isKindOfClass: [Rectangle class]] == YES ) {

printf(            "square is a kind of rectangle class\n" );

}

//            true

if            ( [sq isKindOfClass: [NSObject class]] == YES ) {

printf(            "square is a kind of object class\n" );

}

//            respondsToSelector

//            true

if            ( [sq respondsToSelector: @selector( setSize: )] == YES ) {

printf(            "square responds to setSize: method\n" );

}

//            false

if            ( [sq respondsToSelector: @selector( nonExistant )] == YES ) {

printf(            "square responds to nonExistant method\n" );

}

//            true

if            ( [Square respondsToSelector: @selector( alloc )] == YES ) {

printf(            "square class responds to alloc method\n" );

}

//            instancesRespondToSelector

//            false

if            ( [Rectangle instancesRespondToSelector: @selector( setSize: )] ==           YES ) {

printf(            "rectangle instance responds to setSize: method\n" );

}

//            true

if            ( [Square instancesRespondToSelector: @selector( setSize: )] ==           YES ) {

printf(            "square instance responds to setSize: method\n" );

}

//            free memory

[rec            release];

[sq            release];

第一节总括

这一节是对Objective-C(以后简称OC)的简要介绍,目的是使读者对OC有一个概括的认识。

1.面象的读者

在阅读本文之前,应具备使用与C类似的编程语言(如C,C++,JAVA)的一些经验,同时熟悉面向对象编程。

2.OC简介

OC是以SmallTalk为基础,建立在C语言之上,是C语言的超集。20世纪80年代早期由 Brad J.Cox设计,2007年苹果公司发布了OC 2.0,并在iPhone上使用OC进行开发。

3.OC学习内容

学习的内容主要包括语法和Cocoa框架两部分。本文主要对语法进行介绍。

4.IDE

编写OC程序最主要的编译环境是Xcode,它是苹果官方提供的IDE,官网中的SDK包括Xcode,可以通过下载SDK来获得它。但是Xcode只支持MacOSX,所以如果要在其它环境下编写OC程序,要使用其它IDE。Linux/FreeBSD用GNUStep,Windows NT5.x(2000,XP)要先安装cywin或mingw,然后安装GNUStep。同时仅仅通过文本编辑器,GCC的make工具也可以用于开发。

注:如果要使用到Cocoa的话,只能在Apple公司的Xcode上。

5.框架

OC编程中主要用到的框架是Cocoa,它是MacOS X中五大API之一,它由两个不同的框架组成FoundationKit 和ApplicationKit。Foundation框架拥有100多个类,其中有很多有用的、面向数据的低级类和数据类型,如NSString,NSArray,NSEnumerator和NSNumber。ApplicationKit包含了所有的用户接口对象和高级类。这些框架本文不做重点介绍,如果要深入了解可以去看Xcode自带的文档。

6.特别之处

初次接触OC时,会发现许多和其它语言不同的地方,会看到很多的+,-,[ ,] ,@,NS等符号,这些符号在以后的编程中将经常看到,这部分内容在第二节中介绍。先熟悉一下OC的代码:

#import            "ClassA.h"

#import

int            main( int argc, const char *argv[] ) {

ClassA            *c1 = [[ClassA alloc] init];

ClassA            *c2 = [[ClassA alloc] init];

//            print count

printf(            "ClassA count: %i\n", [ClassA initCount] );

ClassA            *c3 = [[ClassA alloc] init];

//            print count again

printf(            "ClassA count: %i\n", [ClassA initCount] );

[c1            release];

[c2            release];

[c3            release];

return            0;

}

除了这些语言要素上的不同,OC也提供了一些很好的特性,如类别,扮演(Posing)等,这些在运行时的特性使得编程更加灵活。

7.优缺点

每一个语言都有其优缺点,OC也不例外,这就要求在选择语言时权衡利弊。对于OC,只要善于利用它的优点,你会发现它是一个简单,灵活,高效的语言。以下列举了它的一些特点:

优点:类别、扮演(Posing)、动态类型、指针计算、弹性信息传递、不是一个过度复杂的c衍生语言、可通过Objective-c++与c++结合

缺点:没有命名空间、没有操作符重载、不像c++那样复杂

第二节:对C的扩展

1.扩展名

OC是ANSI版本C的一个超集,它支持相同的C语言基本语法。与C一样,文件分为头文件和源文件,扩展名分别为.h和.m。如果要加入c++的语法,需要用到.mm,这里不做介绍。

.h

头文件。头文件包涵类的定义、类型、方法以及常量的声明

.m

源文件。这个典型的扩展名用来定义源文件,可以同时包含C和Objective-C的代码。

2.#import

在OC里,包含头文件有比#include更好的方法#import。它的使用和#include相同,并且可以保证你的程序只包含相同的头文件一次。相当于#include+ #pragma once的组合。

例如要包含Foundation框架中的Foundation.h文件,可以像下面这样。

#import

注:每个框架有一个主的头文件,只要包含了这个文件,框架中的所有特性都可以被使用。

[email protected]符号

@符号是OC在C基础上新加的特性之一。常见到的形式有@”字符串”,%@ , @interface,@implement等。@”字符串”表示引用的字符串应该作为Cocoa的NSString元素来处理。@interface等则是对于C的扩展,是OC面向对象特性的体现。

注:这里提一个小技巧,只要看到@符号,就可以认为它是对于C的一个扩展。

4.NSLog()

在OC中用的打印函数是NSLog(),因为OC是加了一点”特殊语料”的C语言,所以也可以用printf()但是NSLog()提供了一些特性,如时间戳,日期戳和自动加换行符等,用起来更方便,所以推荐使用NSLog()。下面是两种输出的对比。

使用NSLog()输出任意对象的值时,都会使用%@格式说明。在使用这个说明符时,对象通过一个名为description的方法提供自己的NSLog()格式。

下面分别是使用NSLog()和使用printf()的相应输出:

2010-10-15 14:54:21。426            10_15[1973:207] Hello World!

Hello            World!

注:NS前缀告诉你函数来自Cocoa而不是其他工具包。

5.BOOL

BOOL是OC中的布尔类型,它和C中的bool有如下区别

BOOL

YES(1),NO(0)

bool

true(!0),false(0)

6.id

这是OC新加的一个数据类型,它是一般的对象类型,能够存储任何类型的方法。

7.nil

在OC中,相对于C中的NULL,用的是nil。这两者是等价的。下面是nil的定义。

#define            nil NULL

第三节:创建对象

1.接口和实现

在OC中定义一个类需要有两个部分:接口和实现。接口文件包含了类的声明,定义了实例变量和方法。实现文件包含了具体的函数的实现代码。下图显示了一个叫 MyClass的类,它继承自NSObject基类。类的定义总是从@interface开始到@end结束。在类名后面的是父类的名称。实例变量被定义 在两个花括号之间。在实例变量下面的是方法的定义。一个分号用来结束一个变量或者方法。

下面的代码显示了MyClass这个类的实现代码。就像类的定义规则一样,类实现文件也被两个标识框起来,一个是 @implementation,还有一个是@end。这两个指令标识符告诉编译器程序从哪里开始编译到哪里结束。类中的方法名称的定义和它接口文件中的 定义是一样的,除了实现文件中有具体的代码以外。

@implementation            MyClass

-            (id)initWithString:(NSString *) aName

{

if            (self = [super init]) {

count            count = 0;

data            = nil;

name            = [aName copy];

return            self;

}

}

+            (MyClass *)createMyClassWithString: (NSString *) aName

{

return            [[[self alloc] initWithString:aName] autorelease];

}

@end

当你要把一个对象保存进变量,要使用指针类型。OC同时支持强和弱变量对象。强类型对象在变量类型定义的时候包含了类名。弱对象使用id类型作为实例变量。下面的例子同时显示了定义MyClass中的强弱两种类型的变量

MyClass*             myObject1;    // Strong typing

id                   myObject2;    // Weak typing

2.方法

一个方法定义包含了方法类型,返回类型,一个或者多个关键词,参数类型和参数名。在OC中一个类中的方法有两种类型:实例方法,类方法。实例方法前用(-)号表明,类方法用(+)表明,通过下图可以看到,前面有一个(-)号,说明这是一个实例方法。

在OC中,调用一个方法相当于传递一个消息,这里的消息指的是方法名和参数。所有的消息的分派都是动态的,这个体现了OC的多态性。消息调用的方式是使用方括号。如下面的例子中,向myArray对象发送insertObject:atIndex:这个消息。

[myArray            insertObject:anObj atIndex:0];

这种消息传递允许嵌套

[[myAppObject            getArray] insertObject:[myAppObject getObjectToInsert] atIndex:0];

前面的例子都是把消息传递给实例变量,你也可以把消息传递给类本身。这时要用类方法来替代实例方法 。你可以 把他想象成静态C++类(当然不完全相同)。

类方法的定义只有一个不一样那就是用加号(+)代替减号(-)。下面就是使用一个类方法。

NSMutableArray*              myArray = nil;    // nil is essentially the same as NULL

//            Create a new array and assign it to the myArray variable.

myArray =            [NSMutableArray arrayWithCapacity:0];

3.属性

属性提供了比方法更方便的访问方式。通过property标识符来替代getter和setter方法。使用方法就是在类接口文件中用@property标识符,后面跟着变量的属性,包括 copy, tetain, assign ,readonly , readwrite,nonatomic,然后是变量名。同时在实现文件中用@synthesize标识符来取代getter和setter方法。

@property            BOOL flag;

@property            (copy) NSString* nameObject;

//            Copy the object during assignment.

@property            (readonly) UIView* rootView;  // Create only a getter method

接口文件中使用@property

@synthesize            flag,nameObject,rootView;

实现文件中使用@synthesize

属性的另一个好处就是,可以使用点(.)语法来访问,如下所示:

myObject.flag            = YES;

CGRect              viewFrame = myObject.rootView.frame;

第四节:继承

继承的语法如下,冒号后的标识符是需要继承的类。

@interface            Circle : NSObject

1.不支持多继承

要注意的是OC只支持单继承,如果要实现多继承的话,可以通过类别和协议的方式来实现,这两种方法将在后面进行介绍。

2.Super关键字

OC提供某种方式来重写方法,并且仍然调用超类的实现方式。当需要超类实现自身的功能,同时在前面或后面执行某些额外的工作时,这种机制非常有用。为了调用继承方法的实现,需要使用super作为方法调用的目标。下面是代码示例:

@implementation            Circle

-(void)setFillColor:            (ShapeColor) c

{

if            (c== kRedColor){

c            = kGreenColor;

}

[super            setFillColor: c];

}

@end

Super来自哪里呢?它既不是参数也不是实例变量,而是由OC编译器提供的某种神奇功能。向super发送消息时,实际上是在请求OC向该类的超类发送消息。如果超类中没在定义该消息,OC将按照通常的方式在继承链中继续查找对应的消息。

第五节:对象初始化

1.分配与初始化

对象的初始化有两种方法:一种是[类名new], 第二种是[[类名 alloc]init]。这两种方法是等价的,不过,通常的Cocoa惯例是使用alloc和init,而不使用new.一般情况下,Cocoa程序员只是在他们不具备足够的水平来熟练使用alloc和init方法时,才将new作为辅助方法使用。

[[类名alloc]init]有两个动作。alloc是分配动作,是从操作系统获得一块内存并将其指定为存放对象的实例变量的位置。同时,alloc方法还将这块内存区域全部初始化为0。与分配动作对应的是初始化。有如下两种初始化写法。

Car *car =            [[Class alloc] init];

写法1

Car            *car = [Car alloc];

[car            init];

写法2

应该使用第一种写法,因为init返回的对象可能不是以前的那个。

2.编写初始化方法

下面是一段初始化的代码

-(id)init

{

if(self = [super            init]){

engine = [Engine            new];

}

}

使用self= [super init]的作用是使超类完成它们自己的初始化工作。同时因为init可能返回的是不同的对象,实例变量所在的内存位置到隐藏的self参数之间的跳离又是固定的,所以要这样使用。

注:这部分可以参考书[1]144页。

第六节:协议

这里的协议是正式协议,相对的还有非正式协议,这在类别一节中有介绍。正式协议是一个命名的方法列表。它要求显式地采用协议。采用协议意味着要实现协议的所有方法。否则,编译器会通过生成警告来提醒你。

1.声明协议

@protocol            NSCopying

-(id)            copyWithZone:(NSZone            *)zone;

@end

2.采用协议

@interface Car :            NSObject

{

// instance            variables

}

@end

协议可以采用多个,并且可以按任意顺序列出这些协议,没有什么影响。

3.OC 2.0的新特性

OC2.0增加了两个新的协议修饰符:@optional和@required,因此你可以像下面这样编写代码:

@protocol            BaseballPlayer

-(void)drawHugeSalary;

@optional

-(void)slideHome;

-(void)catchBall;

@required

-(void)swingBat;

@end

因此,一个采用BaseballPlayer协议的类有两个要求实现的方法:-drawHugeSalary和-swingBat,还有3个不可选择实现的方法:slideHome,catchBall和throwBall。

第七节:委托

Cocoa中的类经常使用一种名为委托(delegate)的技术,委托是一种对象,另一个类的对象会要求委托对象执行它的某些操作。常用的是,编写委托 对象并将其提供给其他一些对象,通常是提供给Cocoa生成的对象。通过实现特定的方法,你可以控制Cocoa中的对象的行为。

通过下面的例子,可以更清楚地理解委托的实现原理。其中A对象需要把一些方法委托给其它对象来实现,例子中就是对象B,B实现了含A对象特定方法的协议ADelegate,从而可以在B中实现A委托的方法。

@protocol            ADelegate

-            (void)aDelegateMethod;

……

@end

@interface            A : NSObject {

……

id            delegate;

}

@property            (readwrite, assign)

id            delegate;

……

@end

@implementation            A

@synthesize            delegate;

-            (void)aMethod{

[delegate            aDelegateMethod];

……

}

@end

A类

@interface            B : NSObject

@end

@implementation            B

-            (id)init {

……

[[A            sharedA] setDelegate:self];

}

-            (void)aDelegateMethod{   //B中实现A委托的方法

……

}

@end

B类

注:实现委托还可以使用类别,在第八节中将做介绍

第八节: 类别

类别允许你在现有的类中加入新功能,这些类可以是框架中的类,并且不需要扩充它。

1.声明类别

@interface            NSString (NumberConvenience)

-(NSNumber *)            lengthAsNumber;

@end

该声明表示,类别的名称是NumberConvenience,而且该类别将向NSString类中添加方法。

2.实现类别

@implementation            NSString (NumberConvenience)

-(NSNumber            *) lengthAsNumber

{

unsigned int            length = [self length];

return            ([NSNumber numberWithUnsignedInt: length]);

}

@end

3.局限性

类别有两方面的局限性。第一,无法向类中添加新的实例变量。类别没有位置容纳实例变量。第二,名称冲突,即类别中的方法与现有的方法重名。当发生名称冲突时,类别具有更高的优先级。这点可以通过增加一个前缀的方法解决。

4.非正式协议和委托类别

实现委托除了第七节中应用协议的方式,还可以使用类别。具体做法就是把委托对象要实现的方法声明为一个NSObject的类别。如下面的代码所示:

@interface            NSObject(NSSomeDelegateMethods)

-(void)someMethod;

@end

通过将这些方法声明为NSObject的类别,使得只要对象实现了委托方法,任何类的对象都可以成为委托对象。创建一个NSObject的类别称为“创建 一个非正式协议”。非正式协议只是一种表达方式,它表示“这里有一些你可能想实现的方法”,第六节介绍的协议可以叫做正式协议。

非正式协议的作用类似于使用许多@optional的正式协议,并且前者正逐渐被后者所代替。

5.选择器

选择器只是一个方法名称,它以OC运行时使用的特殊方式编码,以快速执行查询。你可以使用@selector()预编译指令指定选择器,其中方法名位于圆括号中。如一个类中setEngine:方法的选择器是:@selector(setEngine:)。

因为选择器可以被传递,可以作为方法的参数使用,甚至可以作为实例变量存储。这样可以生成一些非常强大和灵活的构造。

第九节:Posing

Posing有点像类别,但不太一样。它允许你扩充一个类,并且全面性地扮演(pose)这个超类。例如:你有一个扩充NSArry的 NSArrayChild对象。如果你让NSArrayChild扮演NSArry,则在你的代码中所有的NSArray都会自动被替代为 NSArrayChild.

@interface            FractionB: Fraction

-(void)            print;

@end

@implementation            FractionB

-(void)            print {

printf(            "(%i/%i)", numerator, denominator );

}

@end

Fraction.m

int            main( int argc, const char *argv[] ) {

Fraction            *frac = [[Fraction alloc] initWithNumerator: 3 denominator: 10];

//            print it

printf(            "The fraction is: " );

[frac            print];

printf(            "\n" );

//            make FractionB pose as Fraction

[FractionB            poseAsClass: [Fraction class]];

Fraction            *frac2 = [[Fraction alloc] initWithNumerator: 3 denominator: 10];

//            print it

printf(            "The fraction is: " );

[frac2            print];

printf(            "\n" );

//            free memory

[frac            release];

[frac2            release];

return            0;

}

Main.m

The            fraction is: 3/10

The            fraction is: (3/10)

输出

这个程序的输出中,第一个fraction会输出3/10,而第二个会输出(3/10),这是FractionB中实现的方式。poseAsClass这个方法是NSObject的一部分,它允许子类扮演超类。

第十节:动态识别 (Dynamictypes)

下面是应用动态识别时所用到的方法:

-(BOOL)isKindOfClass:            classObj

是否是其子孙或一员

-(BOOL)isMemberOfClass:            classObj

是否是其一员

-(BOOL)respondsToSelector:            selector

是否有这种方法

+(BOOL)instancesRespondToSelector:            selector

类的对象是否有这种方法

-(id)performSelector:            selector

执行对象的方法

通过下面的代码可以更清楚地理解动态类型的使用:

import            "Square.h"

#import            "Rectangle.h"

#import

“     int            main( int argc, const char *argv[] ) {

Rectangle            *rec = [[Rectangle alloc] initWithWidth: 10 height: 20];

Square            *sq = [[Square alloc] initWithSize: 15];

//            isMemberOfClass

//            true

if            ( [sq isMemberOfClass: [Square class]] == YES ) {

printf(            "square is a member of square class\n" );

}

//            false

if            ( [sq isMemberOfClass: [Rectangle class]] == YES ) {

printf(            "square is a member of rectangle class\n" );

}

//            false

if            ( [sq isMemberOfClass: [NSObject class]] == YES ) {

printf(            "square is a member of object class\n" );

}

//            isKindOfClass

//            true

if            ( [sq isKindOfClass: [Square class]] == YES ) {

printf(            "square is a kind of square class\n" );

}

//            true

if            ( [sq isKindOfClass: [Rectangle class]] == YES ) {

printf(            "square is a kind of rectangle class\n" );

}

//            true

if            ( [sq isKindOfClass: [NSObject class]] == YES ) {

printf(            "square is a kind of object class\n" );

}

//            respondsToSelector

//            true

if            ( [sq respondsToSelector: @selector( setSize: )] == YES ) {

printf(            "square responds to setSize: method\n" );

}

//            false

if            ( [sq respondsToSelector: @selector( nonExistant )] == YES ) {

printf(            "square responds to nonExistant method\n" );

}

//            true

if            ( [Square respondsToSelector: @selector( alloc )] == YES ) {

printf(            "square class responds to alloc method\n" );

}

//            instancesRespondToSelector

//            false

if            ( [Rectangle instancesRespondToSelector: @selector( setSize: )] ==           YES ) {

printf(            "rectangle instance responds to setSize: method\n" );

}

//            true

if            ( [Square instancesRespondToSelector: @selector( setSize: )] ==           YES ) {

printf(            "square instance responds to setSize: method\n" );

}

//            free memory

[rec            release];

[sq            release];

return            0;

}”

输出:

square            is a member of square class

square            is a kind of square class

square            is a kind of rectangle class

square            is a kind of object class

square            responds to setSize: method

square            class responds to alloc method

square            instance responds to setSize: method

return            0;

}”

输出:

square            is a member of square class

square            is a kind of square class

square            is a kind of rectangle class

square            is a kind of object class

square            responds to setSize: method

square            class responds to alloc method

square            instance responds to setSize: method

时间: 2024-08-28 15:00:01

OC基础教程之基本语法的相关文章

黑马程序员---OC基础5【点语法】【@property关键字】【@synthesize关键字】【id类型】【动态类型】【构造方法】

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- [点语法] 1 1.使用“点语法” 2 Student *stu= [[Student alloc]init]; 3         //调用set方法 4         [stu setAge:20]; 5         [stu setName:@"Jone"]; 6         //点语法:xcode的特性,Xcode帮我们做了代码替换 7         //点语法,

OC基础教程

1argc为启动参数个数,因为启动参数第一个是工程名称,所以argc必大于等于1 2可以在方法中的注释哩加入以TODO: MARK: FIXME: ???:等开头的文本,这些作为程序员所做的记号放入到功能菜单中. 3runloop一直处于阻塞状态,直至某些有趣的事情发生为止. 4非正式协议是NSObject的一个类别.它能够列出对象能够响应的方法. 5文件加载与保存 6键值编码 7NSPredicate 8暴力调试:在程序中放入输出语句(如NSLog)来输出程序的控制流程和一些数据值. 10一些

OC基础教程10-NSNumber详细

NSNumber简介 NSNumber是数字的对象形式,因为在OC的数组和字典中只允许存放对象,所以我们有时候需要转化 我们普通的类型是   123 那么 NSNumber类型的是  @123,它是一个对象 1.把int float double等类型转化为 NSNumber类型 查看系统的API我们可以看见有如下的方法可以生成NSNumber类型: + (NSNumber *)numberWithChar:(char)value; + (NSNumber *)numberWithUnsigne

OC基础教程11-结构体对象NSValue与NSString之间的转换

NSValue简介 NSValue是结构体的对象类型,它为我们封装了一系列的很好的方法 可以方便的把结构体转为对象类型 1.把普通的结构体转化为NSValue类型 + (NSValue *)valueWithPoint:(NSPoint)point; + (NSValue *)valueWithSize:(NSSize)size; + (NSValue *)valueWithRect:(NSRect)rect; + (NSValue *)valueWithEdgeInsets:(NSEdgeIn

【《Objective-C基础教程 》笔记ch04】(五)OC中的继承inheritance机制

一.为什么需要继承 使用继承一方面继承了父类的特性,另一方便解决了重复代码维护问题. 二.继承之语法 1. @interface 子类名:父类名 2. OC只支持单继承,不支持继承多个父类. 3. 重构--移植和优化代码. 三. 继承的工作机制 1. 方法调度 子类拥有一个指向它父类的引用指针,消息传递时,OC的方法调度机制使用该信息来找到正确的实现方法,查找过程是现在子类中找,找不到再到父类中继续找. 2. 实例变量 1)继承实例源码 @interface Shape : NSObject {

【《Objective-C基础教程 》笔记ch03】(四)OC中的OOP

一.声明类接口步骤: 1.声明一个类接口,使用@interfacekeyword加上类名称. 2.用  { 实例变量 }  来定义各种数据成员. 3.方法声明,採用中缀符语法声明一个c函数,用到了冒号 : . 二.声明类接口实例: //声明圆形circle类接口 @interface Circle : NSObject { ShapeColor fillColor;//每次创建新的Circle对象后,对象中都包含这两个元素--类的实例变量 ShapeRect bounds; }//指定实例变量

【《Objective-C基础教程 》笔记ch05】(六)OC中的复合机制Composition

 1.复合通过包含作为实例变量的的对象指针实现的.        @interface Unicycle : NSObject        {           Pedal*pedal;           Tire*tire;         }//Pedal和tire通过复合的方式组成了Unicycle 2.存取方法--用来读取或者改变某个对象属性的方法. #import <Foundation/Foundation.h> @interface Car : NSObject { Eng

OC基础 点语法的使用

OC基础 点语法的使用 1.创建一个Student类继承于NSObject,Student.h文件 #import <Foundation/Foundation.h> @interface Student : NSObject { NSString *_name; NSString *_nickName; } //@property定义属性 //1,它给该类声明了一个属性名+下划线的变量 //2,它给该类声明了一个getter方法和一个setter方法 //修饰符 //atomic和nonat

【《Objective-C基础教程 》笔记】(七)OC的关键字用意小结

1.  #import #import <> 从system目录中查找头文件,不会检查当前目录. #import "" 首先在当前目录中查找,如果未找到,则在Xcode设置的预处理程序搜索路径中查找文件. #import 指令可以防止头文件被重复包含 2. typedef 用于给一个类型定义新名称 或 简化较为复杂的类型声明. 3. #define 用于宏定义预处理. 4. 定义类 *.h 用于定义类的头文件 @interface 类 : 父类 @end *.m Objec