本文讨论了Objective-C语言的核心语法。本节开始详细介绍一些特定的语法。如您所料,涉及到定义和类。推荐主题:iPhone应用程序开发初探类并不特殊在Smalltalk中,类是具有某些特征的对象。在Objective-C中也是如此。一个类就是一个对象,对象响应消息。Objective-C和C++都将对象分配和初始化分开。在C++中,对象是通过new操作分配的。在Objective-C中,这是通过向类发送分配消息来完成的——调用malloc()或等效方法。C++中的初始化是通过调用与类同名的函数来完成的。Objective-C不区分初始化方法和其他方法,但按照约定默认的初始化方法是初始化。当你声明一个方法让实例响应时,类方法的声明通常以“-”和“+”开头。在文档中为这些消息使用一些前缀是很常见的,因此您也可以说+alloc和-init来暗示alloc被传递给一个类,而init被传递给一个实例。与其他一些面向对象语言一样,Objective-C中的类是对象工厂。大多数类自己不实现+alloc,而是从它们的父类继承。在NSObject中,父类。在大多数Objective-C程序中,+alloc方法调用+allocWithZone:。以NSZone作为参数,一个包含一些对象分配策略的C结构。回到1980年代,当NeXTstep使用Objective-C在只有8MB内存和25MHZ的CPU机器上实现设备驱动程序和GUI时,NSZone对于优化非常重要。同时,这一点或多或少被Objective-C程序员忽略了。(可能变得像NUMA架构一样流行和更常见。)许多不错的功能之一是对象创建语义由库定义的想法,语言不是类的集群。当您将-init消息传递给对象时,它会返回一个已初始化的对象。这可能是您将消息发送到的对象,但不一定是。这与其他初始化程序一致。很可能一些通用类的特殊子类在不同数据上更有效。实现此功能的常用方法称为isa-swizzling。正如我之前提到的,Objective-C对象是C结构,其第一个元素是指向类的指针。这个元素是可访问的,就像其他实例变量一样;您可以通过分配新值在运行时更改对象的类。当然,如果您的对象类设置在内存中有不同的布局,那么这些设置可能会出现严重错误。但是,你可以通过一个父类来定义布局,通过子集的集合来定义行为,例如,这种技术被用在标准化的字符串类(NSString)中,它有不同的文本字符集,静态的东西,以及其他各种实例.因为类是对象,所以您可以像操作对象一样操作它们。例如,您可以将它们放入集合中。当我有一些需要由不同类的实例处理的输入事件时,我使用这种格式。您需要创建一个将事件名称映射到类的目录,然后为每个输入事件实例化一个对象。如果您在库中执行此操作,它允许代码的用户轻松注册他们自己的处理程序。类型和指针Objective-C不允许在堆栈上定义对象。但并非如此——在堆栈上定义对象是很有可能的,但有点困难,因为它打破了一个关于内存管理的假设。因此,每个Objective-C对象都是一个指针。有些类型是由Objective-C定义的;这些类型在头文件中定义为C类型。Objective-C中最常见的三种类型是id、Class和SEL。id是指向Objective-C对象的指针。相当于C语言中的void*。您可以将任何对象指针类型映射到它,并将它映射到其他对象指针类型。您可以将任何消息传递给id,但如果不支持,将返回运行时异常。类是指向Objective-C类的指针。类是对象,所以它们也可以接收消息。类名是一种类型,而不是可变的。标识符NSObject是NSObject实例的类型,但也充当消息接收者。可以得到一个类,如下:[NSObjectclass];向NSObject类发送一个+class消息,然后返回一个指向表示该类的类结构的指针。正如我们在本系列的第2部分中看到的,这对我们的审查[FS:PAGE]非常有用。第三种类型SEL表示选择器——表示方法名称的抽象。您可以在编译时通过@selector()直接创建它,或者在运行时从C字符串调用运行时库函数,或者使用OpenStepNSSelectorFromString()函数,它为Objective-C字符串提供一个选择器。此技术允许您按名称调用方法。您可以在C中通过使用dlsym()之类的东西来执行此操作,但在C++中则大不相同。在Objective-C中,你可以这样做:[objectperfomSelector:@selector(doSomething)];这等同于:[objectdoSomething];显然,第二种格式稍微快一些,因为第一种发送了两个消息。稍后,我们将看到选择器处理的一些细节。C++没有与id相同的类型。因为对象总是可以被键入的。在Objective-C中,您可以选择类型系统。以下两个都是有效的:idobject=@”astring”;NSString*string=@”astring”;常量字符串实际上是NSConstantString类的实例,它是NSString的子类。将它引用到NSString*可以启用编译时类型检查消息和公共实例变量的存储(这在Objective-C中从未使用过)。请注意,您可以通过以下方式更改此设置:NSArray*array=(NSArray*)string;如果向数组发送消息,编译器将检查NSArray是否可以接收消息。这不是很有用,因为对象是一个字符串。如果您发送由NSArray和NSString实现的消息,它可能会起作用。如果您发送NSString未实现的消息,则会抛出异常。强调Objective-C和C++之间的区别似乎很奇怪。Objective-C具有类型值语法,而C++具有类型变量语法。在Objective-C中,对象类型是特定于对象的属性。在C++中,类型取决于变量的类型。在C++中,当你将指向一个对象的指针赋值给一个变量并定义一个指向超类的指针时,这两个指针可能具有不同的值(这可以通过多重继承来实现,而Objective-C不支持这种方式。)定义类一个Objective-C类定义有一个接口和一个实现部分。与C++有相似之处,但两者略有混淆。Objective-C中的接口只定义位并且需要显式公开。出于实现的原因,这包括大多数实现中的私有实例变量,因为除非知道类有多大,否则无法从类继承。一些最近的实现,如Apple的64位运行时,没有此限制。Objective-C对象的接口如下:@interfaceAnObject:NSObject{@privateintintegerivar@publicidanotherObject;}+(id)aClassMethod;-(id)anInstanceMethod:(NSString*)aStringwith:(id)anObject@end***该行包含3个部分。标识符AnObject是新类的名称。冒号后的名称是NSObject。(这是可选的,但每个Objective-C对象都应该扩展NSObject)。括号中的名称是由类实现的协议(类似于Java中的接口)。正如C++实例变量(C++中的字段)可以访问修饰符一样,与C++不同的是,这些修饰符以@为前缀,以避免与C标识符发生冲突。Objective-C不支持多重继承,所以只有一个父类。因此,对象第一部分的布局始终与父类实例的布局一致。这曾经被定义为动态的,这意味着改变一个类中的实例变量需要重新编译它的所有子类。在较新的运行时中不需要此限制,因为访问实例变量的成本略高。这个决定的另一个影响是Objective-C的其他特性之一。struct_AnObject{@defs(AnObject);};@def表示将特定对象的所有字段都插入到此结构中,因此struct_AnObject和AnObject类的实例具有相同的内存结构。例如,您可以使用此规则直接访问实例变量。出于性能原因,一个常见的用法是允许C函数直接操作Objective-C对象。正如我之前暗示的那样,与此功能相关的另一件事是在堆栈上创建对象的能力。由于结构和对象在[FS:PAGE]内存布局中具有相同的结构,因此您可以简单地创建结构,将其指针设置为正确的类,然后将指针映射到对象指针。然后你可以把它当作一个对象来使用,尽管你必须小心没有什么东西可以让指针越界。(我从未在现实世界中使用过这种方法,这只是理论上可行。)与C++不同,Objective-C没有私有或受保护的方法。Objective-C对象上的任何方法都可以被其他对象调用。如果您不在接口中声明方法,则它是非正式私有的。您将收到运行时警告:对象未响应此消息,但您仍然可以调用它。接口与C中的头声明非常相似。但它仍然需要一个实现,这并不奇怪,并且可以使用@implementation来定义。@implementationAnObject+(id)aClassMethod{...}-(id)anInstanceMethod:(NSString*)aStringwith:(id)anObject{...}@end注意参数类型是具体的,在括号里。这是重用C中的映射语法来显示值映射到类型;它们可能不是类型。映射时应用完全相同的规则。这意味着不兼容的对象指针类型之间的映射将导致警告(不是错误)。内存管理传统上,Objective-C不提供任何内存管理。在早期版本中,对象类实现了一个调用malloc()来创建新对象的+new方法。使用完该对象后,传递一个-free消息。任何继承自NSObject的对象都会响应-retain和-release消息。使用完该对象后,您将传递一个-free消息。OpenStep添加了参考计算。每个继承自NSObject的对象都会响应-retain和-release消息。当你想保留指向一个对象的指针时,你发送一个-retain消息。使用完毕后,您可以发送-release消息。这种设计有一个微妙的问题。通常您不需要保留指向对象的指针,但您也不想释放它。一个典型的例子是当返回一个对象时,调用者需要保留一个指向该对象的指针,但你并不想这样做。这个问题的解决方案是NSAutoreleasePool类。使用-retain和-release,NSObject也响应-autorelease消息。当您发送其中之一时,它会在当前的自动释放池中注册。当池对象未注册时,它会向之前收到-autorelease消息的每个对象发送一个-release消息。在OpenStep应用程序中,NSAutoreleasePool的实例在循环开始时创建并在结束时销毁。您还可以创建自己的实例来自动释放对象。这种机制减少了C++所需的一些复制。这真的不值得,在Objective-C中,可变性是对象的属性,而不是引用。在C++中,有const指针和非常量指针。不允许在const对象上调用非常量方法。这并不能保证对象不会被更改——只是因为您不想更改。在Objective-C中,普通模式定义了一个不可变类和可变子类。NSString就是一个典型的例子;它有一个可变的子类NSMutableString。如果你得到了NSString并想保存它,你可以传递一个-retain消息并保存指针而不复制。相反,您可以将+stringWithString:消息传递给NSString。检查此参数是否可变并返回原始指针。在Apple和GNU运行时,Objective-C都支持存储垃圾回收,这避免了对-retain和-release的需要。对语言的添加在现有框架中并不总是得到很好的支持,需要格外小心使用。总结既然我们已经涵盖了Objective-C语言的核心,我们将在总结的这一部分看一些更高级的主题。【编辑推荐】快速入门Objective-C基本语法iPhone开发入门规则:Objective-C编码规范C++开发者快速学习Objective-C语言核心语法
