维基百科Objective-C(1)
Objective-C 是一个在C语言的基础上加上了SmallTalk的消息机制的通用的,面向对象编程语言。它主要被用在Apple的OS X和iOS操作系统上对应的Cocoa 和Cocoa Touch应用程序接口(API)。
Objective-C在1980年代被发明的。它早期用于NeXT公司的 NeXTSTEP操作系统,后来NeXTSTEP衍生了OS X和 iOS.因为Objective-C具有很好的可移植性,所以它不仅仅被用于Cocoa和Cocoa Touch而且可以被移植或者重新实现于其它的操作系统上。并且可以在任何支持GCC或者Clang的系统上编译。
Objective-C源代码的’implementation’程序文件用.m作为扩展名,而’header/interface’文件则用.h为扩展名(于c语言的头文件一样)。Objective-C++的源文件扩展名为.mm
历史:
Objective-C最开始是被工作与Stepstone公司的Brad Cox和Tom Love开发的。在1981年,他们在ITT Corporation的研发中心接触到了Smalltalk.早期的Objective-C就是在那段时间被开发的。当时Cox正被软件设计和编程中的可重用性的问题困扰着。他意识到类似Smalltalk的开发语言在为构建ITT系统程序员开发环境中有着极其重要的价值。然而,他和Tom Love也意识到在电信通讯系统中向后对 c语言兼容及其重要。于是Cox开始利用c语言写一个预编译器同时增加一些smalltalk的相关特性。他很多就实现了一个相对c进行了面向对象扩张的实现,他称它“OOPC”(Object-Oriented Pre-Compiler).在1982年,Love加入了Schlumberger研究所,以至于有机会获得第一个Smalltalk-80的商业拷贝的授权,从而进一步的影响发展了他们的想法.
为了证明自己的想法可以在实际中被实现,Cox展示了在一个现有的工具做一些实质上的改变来证实可交互组件的可行性。具体来说就是,他们需要对象能以更灵活的方式来支持提供一组有用的库,并允许代码(含有代码中需要的其它附加资源)打包成一个跨平台的单一格式。
最终,Love和Cox经过一系列的努力让PPI(Productivity Products International)对他们的产品进行商业化运作,即让Objective-C编译器和类库相结合。在1986年,Cox发布的Object-Oriented Programming书中主要应用了进化的方法描述了Objective-C的早期形态。尽管他很谨慎的指出,可重用性于语言无关,但是Objective-C与其它语言的特征相比更有优势。
普及于NeXT(Popularization through NeXT):
1988年,NeXT公司从StepStone(它的新名叫PPI,它拥有Objective-C的版权)获得了Objective-C的授权许可同时扩展了GCC编译器来支持Objective-C.后来NeXT基于Objective-C开发了自己的用户接口和Interface Builder即AppKit和Foundation Kit库。虽然后来NeXT的工作站在市场上表现失败,但是它本身却收到了业内广泛的赞誉。这导致NeXT开始舍弃硬件开始聚焦软件产品,即把NeXTstep,OpenStep作为定制编程平台销售。
为了规避GPL条款,NeXT原本打算让Objective-C作为前端接口单独分离出来,以允许用户用GCC对它链接,编译执行。但是这个计划开始后不久就被Richard M.Stallman给否决了,因为Stallman咨询了GNU的律师,同时NeXT也同意Objective-C作为GCC的一部分。
扩展GCC的工作是从StepStone离开后加入NeXT的Steve Naroff主导的。编译器的改变遵循了GPL条款,但是它的runtime库却不是,因为它并没有开放源代码。这导致其它的公司也可以在开源许可协议下发展自己的运行时库。之后,Steve Naroff在Apple主要工作就是构建Objective-C的前端到Clang.
关于Cocoa的开源实现在GNU中被命名为GNUStep,它是基于OpenSten标准开发的。Dennis Glatting在1992年开发了第一个GNU Objective-C的runtime.但是自1993年以来一直都利用GNU Objective runtime的却是在丹麦(Denmark)的一个大学生开发者Kresten Krab Thorup 。Thorup在1993到1996期间也在NeXT工作。
Apple开发和Swift:
1996,NeXT被Apple Computer收购之后,就把OpenStep应用到自己的新操作系统Mac OS X中。这包括Objective-C和NeXT的基于Objective-C的开发者工具,还有interface设计工具,interface Builder(他们俩被整合到一个独立的Xcode应用中)。Apple现在大部分的Cocoa API都是基于OpenStep接口对象,并且最重要的Objective-C环境也被用作主要的开发工具。
在2014年的WWDC上,Apple发布了自己的新语言Swift,它被称为”没有C的Objective-C”.
语法:
Objective-C是基于C一个高级抽象,严格来说它是C的一个超级。这意味着,可用利用Objective-C的编译器编译任何的C程序,而且可以自由的把c代码包含在一个objective-c的类中。
Objective-C的对象语法来自于Smalltalk.所有的 非面向对象相关的语法操作(包括私有变量,预处理,表达式,函数声明和函数调用)都和C完全相同,而且面向对象的语法特征则是对Smalltalk样式的消息机制的实现.
消息:
Objective-C的面向对象模型是基于消息在对象实例中传递的。在Objective-C中并不是调用一个方法,而是发送一个消息。这不同于像C++一样的Simula-Style的编程模型。这两个概念之间的差异就在于如何应用方法或者消息名称来执行。Simula-style语言, 大多数情况下编译器是把方法名作为目标类的一部分来进行绑定。在Smalltalk和 Objective-C里,消息的目标是在runtime被决定的,消息接受者自己会处理消息。一个方法被一个Selector 或SEL所标示--一个以null结尾的字符串来标示它的名字—并用C指针:IMP来实现它。这个消息传递系统并没有类型检查。消息会直接发送给接受者的,但是并不一定保证会有回复,如果没有,它就会很轻易的抛出一个异常。
一个对象发送一个消息在c++中的代码表示如下:
obj->method(argument);
在Objective-C中的表示如下:
[obj method:argument];
这两种编码方法各有优劣。Simula-Style(C++)的面向对象编程语言允许多几次和使用compile-time绑定,以求更快的执行,但是它不能缺省支持动态绑定。并且它强制多有的方法都必须有一个相应的实现,除非他们是抽象的。Smalltalk-Style 被用于Objective-C,它允许消息不被实现,方法实现是在运行时才被决定的。例如,一个消息可能被发送到一个对象集中,就算只有一部分对象会有响应,也不用担心产生runtime错误。消息传递的时候的接收对象也不需要被定义在一个compile time.但是方法的实现依然可以在派生类中被调用。(看下面的”动态类型”部分,有更多高级的动态绑定介绍)
接口和实现(Interfaces and implementations):
Objective-C要求类的接口和实现被划分在不同的代码块中。通常,开发人员会被接口放在头文件中,而实现会放在代码文件中。头文件的后缀一般为’.h’,它类似于C头文件。实现文件,通常后缀为’.m’,也非常近似于c的代码文件。
接口(Interface)
在其它的编程语言中,这通常被叫做”类声明”。
类接口一般定义在头文件中。通常头文件名由类名加头文件后缀组成。如Ball.h,它包含类的接口Ball.
接口声明的形式如下:
@interface classname : superclassname {
// instance variables
}
+ classMethod1;
+ (return_type)classMethod2;
+ (return_type)classMethod3:(param1_type)param1_varName;
- (return_type)instanceMethod1With1Parameter:(param1_type)param1_varName;
- (return_type)instanceMethod2With2Parameters:(param1_type)param1_varName param2_callName:(param2_type)param2_varName;
@end
在上文中,加号(+)表示类方法或者方法可以被类自己调用(即不需要初始化),j减号(-)表示实例方法,它只能被一个类的实例调用。类方法不能直接访问实例变量。
以下代码是同上的C++接口声明形式:
class classname : public superclassname {
protected:
// instance variables
public:
// Class (static) functions
static void * classMethod1();
static return_type classMethod2();
static return_type classMethod3(param1_type param1_varName);
// Instance (member) functions
return_type instanceMethod1With1Parameter (param1_type param1_varName);
return_type instanceMethod2With2Parameters (param1_type param1_varName, param2_type param2_varName=default);
};
注意: instanceMethod2With2Parameters:param2_callName: 展示了selector的参数列表,它并不直接等同于c/c++中的。
返回类型可以是任意的C类型, 通用的Objective-C对象指针,特殊类型的对象指针如:NSArray*, NSImange*, 或者NSString*, 或者一个类类型或者方法类型。默认的返回类型是通用的Objective-C类型id .
方法的参数开始的参数名标记是那个方法名的一部分,在参数标记后跟一个冒号,冒号后面跟着一个预定义类型和参数名。其实参数标记也可以省略。
- (void)setRangeStart:(int)start end:(int)end;
- (void)importDocumentWithName:(NSString *)name withSpecifiedPreferences:(Preferences *)prefs beforePage:(int)insertPage;
实现(Implementation):
接口只是声明类的接口,并没有实现方法:实现代码被写在实现文件中。实现文件通常以”.m”为扩展名,就是最初所指的’消息’。
@implementationclassname
+ (return_type)classMethod
{
// implementation
}
- (return_type)instanceMethod
{
// implementation
}
@end
方法是利用接口声明写的。比较Objective-C和C:
- (int)method:(int)i
{
return [selfsquare_root:i];
}
int function (int i)
{
return square_root(i);
}
语法允许参数别名:
- (int)changeColorToRed:(float)red green:(float)green blue:(float)blue;
[myColor changeColorToRed:5.0green:2.0blue:6.0];
两种Objective-C实现的内部表示由非常大的不同。如果myColor是Color类的对象,实例方法-changeColorToRed:green:blue: 内部表示的标签可能是:_i_Color_changeColorToRed_green_blue .“i”标示引用的是一个实例方法,类名,方法名附加在后,用下划线连接,并用下划线替换冒号。参数顺序是方法名的一部分,不能被改变来适应代码风格或者表达真正的命名参数。
初始化(Instantiation):
一个类一径声明就可被初始化。它首先给一个没有初始化的类实例分配内存,然后初始化这个类实例。如果这两步没有被完成,那这个实例对象就没有完成初始化。这些步骤应该用一行代码完成,以至于不会造成一个被分配了存储空间的对象没有进行初始化(这样做的原因在于防止有中间过程造成分配了存储空间的对象和执行-init方法返回的对象不一样)。
没有参数的默认初始化方法如下:
MyObject *o= [[MyObject alloc] init];
一般的初始化方法:
MyObject *o= [[MyObject alloc]initWithString:myString];
在没有执行自定义初始化方法是,”new”方法通常可被使用,因为它会默认调用alloc-init消息。
MyObject *o= [MyObject new];
也有一些,类方法的初始化方法。像+new, 他们聚合了+alloc和-init,但是不像+new,他们返回的是autoreleased实例对象。某些类初始化方法附带参数的形式如下:
MyObject *o= [MyObject object];
MyObject *o2= [MyObjectobjectWithString:myString];
alloc消息分配足够的内存来保存所有对象的实例变量,并把所有的实例变量设置为零值,然后把类实例存入内存。 在初始化的过程中并没有初始化一个父类的实例对象.
初始化消息执行是在创建实例设置的时候。初始化方法常常这样写:
- (id)init {
self= [super init];
if (self) {
// perform initialization of object here
}
returnself;
}
在上面的例子中,注意返回类型id.该类型在Objective-C中,表示任意对象的指针。
初始化的形式是必须保证父类的初始化方法被执行后再执行自己的初始化。执行过程如下:
1 self = [super init]
发送一个父类实例的初始化消息然后把结果返回给self指针(self指向实例本身)。
2 if (self)
在执行初始化之前需要检测返回的对象指针的有效性。
3 return self
把self的值返回给调用者。
无效对象指针的值是nil;条件语句像”if”,检测nil一样的空指针,以至于如果[super init]返回nil,初始化代码将不会执行。如果在初始化中有一个错误,初始化方法应该执行一些必要的清楚内存的工作,包括发送一个”release”消息给self,同时返回nil表示初始化失败。任务错误的检测都必须在父类初始化之后执行,以确保销毁对象的行为能正确完成。
如果一个类有多于一个的初始化方法,那只能调用这些初始化方法中的一个。其它的应该调用执行的父类初始化方法替代执行。