ObjectiveC基本语法

消息
在objectiveC中,向一个对象发送一个消息的语法为

[ obj method:parameter];

类似的功能在C++中写作

obj->method(parameter);

在java中写作
obj.method(parameter);

在smalltalk中写作
obj method:parameter

显而易见objectiveC和smalltalk的语法基本是相同的。

当有两个或者两个以上的参数时,通常试用以的语法
[ obj method:parameter1 WithSecondParameter:parameter2];

定义一个类的代码放在一个.h文件中,下面是一个例子。
//macdevexample1.h
@interface ClassName:SuperClassName{
        //begin of instance variables
        @private
                int a;
        @protected
                float b;
        @public
                double c;
        //end of instance variables
}
//begin of method declaration
– instanceExampleMethod1;//instance method with no parameter
+ classExampleMethod1;//class method

– exampleMethod2:(int)parameter1; //instance method with 1 parameter
– (int)exampleMethod3:(int)parameter1 WithSecondParameter:(double)parameter2
;//instance method with 2 parameters

– (float)b;//method can have the same name of variable
– (void)setb:(float)_b;
//end of method declaration
@end
//end of macdevexample1.h

ObjectiveC类的定义以@interface开始,@end结束。@end后面不需要加分号,但是加了
也不会有什么大问题。数据部分包含在大括号中,缺省为protected。
ObjectiveC中的方法都为public。在声明方法时在方法命前加-表示是类的实例可以调
用的方法, +表示是类对象可以调用的方法(类似于C++、java里面的static方法)。
ObjectiveC定义类方法的语法同smalltalk类似,而不是像C++/Java一样同algo/C保持
一致。对于从C++、java转过来的开发人员来说可能有些不习惯。

ObjectiveC方法若没有声明返回值则返回id类型数据,id实际上是一个指向所有类的指
针,具体以后讨论。

实现一个类的代码会放在一个.m文件里
#import macdevexample1.h
@implementation ClassName
– instanceExampleMethod1{
//code goes here
}
+ classExampleMethod1{
//code goes here
}
..
@end
在objectiveC中可以试用#import或者#include来包含一个头文件。在使用#import时每
个头文件只会被包含一次,避免了类和变量重复定义的问题。

创建一个类的实例通常使用如下代码
ClassA *objA = [[ClassA alloc] init];
或者
ClassA *objA = [ClassA new];
或者
id objA = [[ClassA alloc] init];

alloc为类方法,负责分配内存(在heap上),然后返回一个id。init是实例方法,负责
初始化,然后返回一个id。同alloc类似的还有+ allocFromZone:(NXZone *)zone方法
,zone会在以后讨论。init也会有带参数的initWithXXX之类的方法。new方法把分配内
存和初始化在一个方法内完成。但土人俺觉得[[ClassA alloc] init]更清晰一点。

C++、java里面的this在objectiveC里面叫self(Smalltalk,Python, Objective
Pascal,Ruby也叫self, VB里面叫Me,Perl里面用$_[0] (俺Perl没学明白,不太敢确
认),PHP用$this表示实例用self表示类)。Java中的super在objectiveC中也叫super。

ObjectiveC中的BOOL类型的值为Yes和No而不是true和false。

Category是ObjectiveC提出的一个对类进行扩展的办法。C#3.0中extension methods的
概念和ObjectiveC的Category类似。

一下是一个例子,我们首先定义一个mitbbsExampleLocation类,文件名为
mitbbsExampleLocation.h。

#import <Cocoa/Cocoa.h>
@interface mitbbsExampleLocation : NSObject {
        int x,y;
}
-(void)moveToX:(int)x andY:(int)y;
@end

下面是implementation。
#import "mitbbsExampleLocation.h"
@implementation mitbbsExampleLocation
-(void)moveToX:(int)x andY:(int)y{
        self->x = x;
        self->y = y;
}
@end

下面定义一个扩展mitbbsExampleLocation类的category,定义的方法就是在类的名字
后面加一个小括号,然后把category的名字写在小括号里面,文件名不太重要,但是最
好为DisplayData.h。
#import "mitbbsExampleLocation.h"

@interface mitbbsExampleLocation (DisplayData)
        -(void) displayLocation;
        -(void) displayX;
        -(void) displayY;
@end

下面是implementation

#import "DisplayData.h"
@implementation  mitbbsExampleLocation (DisplayData)
        -(void) displayLocation{
                printf("x=%d\ty=%d\n", self->x, self->y);
        }
        -(void) displayX{
                NSAutoreleasePool * Pool = [[NSAutoreleasePool alloc] init];
                NSLog([[NSString stringWithFormat:@"%d\n", self->x]
autorelease]);
                [Pool release];
        }
        -(void) displayY{
                //…………….
        }
@end

Protocol是ObjectiveC中实现多重继承的机制。ObjectiveC中有两种protocol,一种是
非正式的,一种是正式的。非正式的protocol只是在文档中规定应该实现的方法。正式
的Protocol和java中的interface类似。在protocol中开发人员可以定义方法但是不能
定义实例变量。
@protocol mitbbsExampleClear
 -(void) Clear;
@end

@interface betterLocation: mitbbsExampleLocation <mitbbsExampleClear>
         -(void) Clear{
                self->x = 0;
                self->y = 0;
        }
@end

ObjectiveC中的消息机制是其面向对象的核心。在objectiveC中,向一个对象发送一条
消息的语法为

[receiver messageName:parameter];

这条命令会被编译器翻译为C代码

objc_msgSend(receiver, @selector(messageName), parameter);

在这里我们第一次遇到selector的概念。selector在objectiveC中的关键字为SEL。

在GNUStep中, SEL的定义为如下C代码。
typedef const struct objc_selector
{
  void *sel_id;
  const char *sel_types;
} *SEL;

由此可见,selector实际上是一个由一个方法名的字符串和一个unique id组成的结构
。所有同名的方法共有一个selector。当向某个对象发送消息时,objectiveC实际上告
诉对象需要调用某个selector对应的方法,该对象会从自己的Class所保存的selector-
method对应列表中查找是否可以相应该selector对应的方法,如果有就该干啥干啥,如
果不存在该selector对应的方法,则在superClass里面查询,如果还是没有则调用
message forward机制。message forward机制以后会讨论。

ObjectiveC会向实例方法传递两个隐含的参数,第一个为self第二个为该方法所对应的
消息的selector。C++仅传递一个隐含参数this。同C相比增加的参数数量会影响性能,
在IA32平台,参数通过栈传递,在PowerPC和x86-64平台参数通过寄存器传递。因此在
PowerPC和x86-64平台增加的参数对性能的影响相对较小。

ObjectiveC的消息机制同C++相比更加灵活,但是相对效率较低。ObjectiveC本质上是
动态的语言,而C++本质上是静态的语言,动态语言的本质导致ObjectiveC的效率必然
比C++低。因此在使用ObjectiveC进行开发时,对性能要求比较高的部分最好不用使用
消息机制(不要试用[receiver message]),而尽可能使用单纯的C语言。

在使用C代码时,可以试用@defs来获取ObjectiveC的Class的结构从而访问ObjectiveC
中的private和protected变量。以上面的mitbbsExampleLocation为例。

首先我们定义一个struct

typedef struct {
 @defs(mitbbsExampleLocation);
} *PLocation;

然后把一个指向mitbbsExampleLocation的指针cast成PLocation类型的变量。
PLocation p = (PLocation)[[mitbbsExampleLocation alloc]init];
p->x = 0;
p->y = 0;

获取一个ObjectiveC类的方法可以使用以下方法。
以mitbbsExampleLocation类的moveToX:andY:为例,首先定义一个指向该函数的指针

void (*moveToXAndY)(id, SEL, int, int);

然后获取方法的地址

moveToXAndY = (void (*)(id, SEL, int, int))[aLocation methodForSelector:@
selector(moveToX:andY:)];

然后moveToXAndY可以和调用普通方法一样调用但需要有连个隐含参数
moveToXAndY(anId, @selector(moveToX:andY:), 1, 2);

methodForSelector方法是在NSObject类中定义的。 其返回值为IMP。IMP的定义为

typedef id (*IMP)(id, SEL, …);

所以IMP就是一个指向返回值为id的ObjectiveC方法。如果返回值为其他类型我们需要
像上面一样自己定义一个方法的指针。

NSObject是目前所有ObjectiveC类的根类。NSObject中的NS表示NeXTSTEP。NSObject是
在NSObject.h文件里定义的。其核心内容如下
@interface NSObject <NSObject> {
    Class       isa;
}

+ (void)load;

+ (void)initialize;
– (id)init;

+ (id)new;
+ (id)allocWithZone:(NSZone *)zone;
+ (id)alloc;
– (void)dealloc;

#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
– (void)finalize;
#endif

– (id)copy;
– (id)mutableCopy;

+ (id)copyWithZone:(NSZone *)zone;
+ (id)mutableCopyWithZone:(NSZone *)zone;

+ (Class)superclass;
+ (Class)class;
+ (void)poseAsClass:(Class)aClass;
+ (BOOL)instancesRespondToSelector:(SEL)aSelector;
+ (BOOL)conformsToProtocol:(Protocol *)protocol;
– (IMP)methodForSelector:(SEL)aSelector;
+ (IMP)instanceMethodForSelector:(SEL)aSelector;
+ (int)version;
+ (void)setVersion:(int)aVersion;
– (void)doesNotRecognizeSelector:(SEL)aSelector;
– (void)forwardInvocation:(NSInvocation *)anInvocation;
– (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;

+ (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector;

#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED
+ (BOOL)isSubclassOfClass:(Class)aClass;
#endif

+ (NSString *)description;

– (Class)classForCoder;
– (id)replacementObjectForCoder:(NSCoder *)aCoder;
– (id)awakeAfterUsingCoder:(NSCoder *)aDecoder;

@end

先把所有的方法放在一边看数据。整个NSObject里面只有一个数据 Class        isa
。Class类型是在objc.h里面通过typedef定义的,原文为

typedef struct objc_class *Class;

所以Class就是一个指向objc_class结构的指针。而objc_class是在 objc-class.h里面
定义的,内容为

struct objc_class {                    
        struct objc_class *isa;
        struct objc_class *super_class;
        const char *name;              
        long version;
        long info;
        long instance_size;
        struct objc_ivar_list *ivars;

        struct objc_method_list **methodLists;

        struct objc_cache *cache;
        struct objc_protocol_list *protocols;
};

这里面最值的注意的是

const char *name;

这一行。objectiveC和C++不同,每个类的名字会被编译器存在编译后的代码中,这样
可以在运行时确定类的名字,这是C++中没有的功能。

struct objc_ivar_list *ivars; 包含了instance variable的信息,例如名字,类型
,位置,这样可以在运行时获得变量的相关信息。objc_ivar_list的定义为

struct objc_ivar_list {
        int ivar_count;
#ifdef __alpha__
        int space;
#endif
        struct objc_ivar ivar_list[1];          /* variable length structure
 */
};

在objc_ivar_list的定义中,定义objc_ivar的代码为
struct objc_ivar {
        char *ivar_name;
        char *ivar_type;
        int ivar_offset;
#ifdef __alpha__
        int space;
#endif
};

struct objc_method_list **methodLists;这一行储存所有的方法的信息。当一个类或
者一个实例收到一个消息的时候会在这个表里面查找有没有相对应的方法。同变量类似
,ObjectiveC可以在运行时获取方法的名字等信息。

struct objc_method_list {
        struct objc_method_list *obsolete;

        int method_count;
#ifdef __alpha__
        int space;
#endif
        struct objc_method method_list[1];      /* variable length structure
 */
};

typedef struct objc_method *Method;

struct objc_method {
        SEL method_name;
        char *method_types;
        IMP method_imp;
};

通过上面的分析,我们可以说isa指针(以及Class变量)和C++的vtable类似但是增加
了很多其他信息,这些信息在二进制代码中也是可读的,使用class-dump(http://www.codethecode.com/projects/class-dump/)直接从objectiveC编译完成的二进制代码生成interface信息。

id也是在objc.h中定义的,代码为

typedef struct objc_object {
        Class isa;
} *id;

所以本质上,id就是一个指向NSObject的实例的指针,由于在objectiveC中所有的类都
是NSObject的子类,所以id可以用来指向任何类的实例。

在很久以前,ObjectiveC中的基类叫做Object,后来NeXTSTEP和Sun一起合作开发了一
个叫做OpenStep的项目,所有的类前面都加上了NS。土人俺不知道这个NS到底是代表(N
)eXT(S)TEP还是(N)eXTSTEP-(S)UN。OpenStep是后来的GNUStep和Cocoa的基础,因此现
在的Cocoa中的类都带着NS。

现在我们已经提到了id,Class,SEL,IMP,BOOL, #import,@interface,@
implementation,@protocol,@end,@private,@protected,@public,@selector(
method_name), @defs(class_name)。

Advertisements