多用类型常量,少用#define预处理命令

来源 ——《Effective Objective-C 2.0 》Matt Galloway

编写代码时经常要定义常量。例如,要写一个UI视图类,此视图显示出来以后就播放动画,然后消失。你可能想把播放动画的时间提取为常量。可能会这么写:

上述预处理指令会把源代码中的ANIMATION_DURTION字符串替换为0.3。这可能就是你想要的效果,不过这样定义出来的常量没有类型信息。“持续”(duration)这个词看上去应该与时间相关,但是代码中又未明确指出。此外,预处理过程会把碰到的所有ANIMATION_DURATION一律替换成0.3。这样的话,假设此指令声明在某个头文件中,那么所有引入了这个头文件的代码,其ANIMATION_DURATION都会被替换。

要想解决此问题,应该设法利用编译器的某些特性才对。有个办法比用预处理指令来定义常量更好。比方说,这样写:

请注意,此方式定义的常量包含类型信息,其好处是清楚地描述了常量的含义。由此可知该常量类型为NSTimeInterval,这有助于为其编写开发文档。如果要定义许多常量,那么这种方式能令稍后阅读代码的人更易理解其意图。 继续阅读多用类型常量,少用#define预处理命令

理解objc_msgSend的作用

来源 ——《Effective Objective-C 2.0 》Matt Galloway

在对象上调用方法是OC中经常使用的功能。用OC的术语来说,这叫做“传递消息”(pass a message)。消息有“名称”(name)或“选择子”(selector),可以接受参数,而且可能还有返回值。

由于OC是C的超集,所以最好先理解C语言的函数调用方式。C语言使用“静态绑定”(static binding),也就是说,在编译期就能决定运行时所应该调用的函数。以下列代码为例:

如果不考虑“内联”(inline),那么编译器在编译代码的时候就已经知道程序中有printHello与printGoodbye这两个函数了,于是会直接生成调用这些函数的指令。而函数地址实际上是硬编码在指令中的。若是将刚才那段代码写成下面这样,会如何呢?

这时就得使用“动态绑定”(dynamic binding)了,因为所要调用的函数直到运行期才能确定。编译器在这种情况下生成的指令与刚才那个例子不同,在第一个例子中,if与else语句里都有函数调用指令。而在第二个例子中,只有一个函数调用指令,不过待调用的函数地址无法硬编码早指令之中,而是要在运行期读取出来。 继续阅读理解objc_msgSend的作用

用枚举表示状态、选项、状态码

来源 ——《Effective Objective-C 2.0 》Matt Galloway

由于OC基于C语言,所以C语言的功能它都有。其中之一就是枚举类型:enum。系统框架中频繁用到此类型,然而开发者容易忽视它。在以一系列常量来表示错误状态码或可组合的选项时,使用枚举为其命名是常见的。由于C++11标准扩充了枚举的特性,所以最新版系统框架使用了“强类型”(strong type)的枚举。没错,OC也能得益于C++11标准。

枚举只是一种常量命名方式。某个对象所经历的各种状态就可以定义为一个简单的枚举集(enumeration set)。比如说,可以用下列枚举表示“套接字连接”(socket connection)的状态:

由于每种状态都用一个便于理解的值来表示,所以这样写出来的代码更易读懂。编译器会为枚举分配一个独有的编号,从0开始,每个枚举递增1。实现枚举所用的数据类型取决于编译器,不过其二进制位(bit)的个数必须能完全表示枚举编号才行。在范例中,由于最大编号是2,所以使用1个字节的char类型就可以。然而定义枚举变量的方式却不太简洁,要依据如下语法编写:

若是每次不用敲入enum而只需写EOCConnectionState就好了。要想这么做,则需要使用typedef关键字重新定义枚举类型:

现在可以用简写的EOCConnectionState来代替完整的enum EOCConnectionState了:

继续阅读用枚举表示状态、选项、状态码

多用字面量语法,少用与之等价的方法

来源 ——《Effective Objective-C 2.0 》Matt Galloway

编写OC程序时,总会用到某几个类,它们属于Foundation框架。虽然从技术上来说,不用Foundation框架也能写出OC代码,但实际上却经常用到此框架。这及各类是NSString、NSNumber、NSArray、NSDictionary。从类名上即可看出各自所表示的数据结构。

OC以语法繁杂而著称,事实上的确实这样。不过,从OC1.0起,有一种非常简单的方式创建NSString对象。这就是“字符串字面量”(string literal),其语法如下:

如果不用这种语法的话,就要以常见的alloc,然后init方法来分配并且初始化NSString对象了。在版本较新的编译器中,也能用这种字面量语法来声明NSNumber、NSArray、NSDictionary类的实例。使用字面量语法(literal syntax)可以缩减源代码长度,使其更为易读。 继续阅读多用字面量语法,少用与之等价的方法

@class与#import

来源 ——《Effective Objective-C 2.0 》Matt Galloway

与C和C++一样,OC也使用“头文件”(header file)与“实现文件”(implementation file)来分离代码。用OC语言编写的“类”(class)的标准方式为:以类名做文件名,分别创建两个文件,头文件后缀用.h,实现文件后缀用.m。创建好一个类以后,其代码看上去是这样的:

用OC语言编写任何类几乎都需要引入Foundation.h。如果不在该类本身引入这个文件的话,那么就要引入其父类所属框架相对应的“基本头文件”(base header file)。例如,在创建iOS应用程序时,通常会继承UIViewController类。而这些子类的头文件需要引入UIkit.h。

现在看来,EOCPerson类还好,虽然其头文件引入了整个Foundation框架。如果此类继承自Foundation框架中的某个类,那么EOCPerson的使用者(consumer)可能会用到其基类中的许过内容。继承UIViewController的那些类也是如此,其使用者可能会用到UIKit中的大部分内容。 继续阅读@class与#import

Objective-C语言的起源

来源 ——《Effective Objective-C 2.0 》Matt Galloway

OC语言和C++、Java等面向对象语言相似,但是在许多方面还是有差别的。比如OC使用消息结构(message structure)而不是函数调用(function calling)。OC语言由Smalltalk演化而来,后者是消息语言的鼻祖。消息结构和函数调用结构之间的区别简单来讲是这样的:

关键区别在于: 使用消息结构的语言,其运行时所应该执行的代码由运行时环境(run time)来决定;而使用函数调用的语言,则由编译器决定。如果范例代码中调用的函数是多态的,那么在运行时候就要按照“虚表”(virtual table:是编程语言为实现“动态派发”(dynamic dispatch)或者“运行时方法绑定”(run time method binding)而采用的一种机制)来检索应该执行哪个函数。如果使用消息结构的语言,不论是否多态,总是在运行时才会去查找索要执行的方法。实际上,编译器甚至不关心接收消息的对象的具体类型,这个也是在运行时处理的,其过程叫做“动态绑定”(dynamic binding)。 继续阅读Objective-C语言的起源