Chapter 2. Objects, Messaging, and the Runtime
<br />
Item 13: Consider Method Swizzling to Debug Opaque Methods
<br />
这一节讲Method Swizzling黑科技。
本身swizzle这个词就有点魔性,不是很常见的词,意思是用酒棒搅甜酒,有一种好好的非要把水搞浑的感觉。
关于Method Swizzling,讲解更详细的是Matttt Thompson <Method Swizzling>一文,地址在这里:http://nshipster.com/method-swizzling/
先看selector, method, implementation三者的定义类型和关系:
- Selector (
typedef struct objc_selector *SEL
): 用于在runtime代表某个method的方法名。方法是一个在runtime注册的c语言string,当类被加载时,selector由编译器自动映射到这个方法上。 - Method (
typedef struct objc_method *Method
): 类定义里封装的“黑箱”方法 - Implementation (
typedef id (*IMP)(id, SEL, …)
): 是一个指向函数入口的指针。第一个参数指向调用函数的类,第二个指针指向一个selector。
每个类都会包含一个dispatch table,里面的键值就是selector,通过IMP指针指向method。这样在runtime就可以通过查表来找到方法了。
Method Swizzling就是在IMP指针上动了手脚。通过修改指针的指向,改变要调用的方法。而且这一过程是在runtime发生的。
具体来说,首先通过class_getInstanceMethod
方法获取想要修改的方法。然后再调用method_exchangeImplementations
来交换两个方法的IMP指针。可以交换两个既有方法,也可以新写一个方法和原来的方法交换,这样调用时,虽然写的是原来方法的方法名,实际上执行的却是新写的方法。因此,相对于写一个继承子类,这是一种更为灵活的为类增加新功能的写法。
需要注意的有:
因为这样的操作只应在初始加载时执行一次,最好在dispatch_once里执行。并且method swizzling并不是atomic的,还要注意concurrency的问题。
没有特殊原因时,最好调用一下method原来的对应的执行。
避免命名冲突,采用前缀。
<br />
Item 14: Understand What a Class Object Is
<br />
这一节解剖了class对象。
typedef struct objc_class *Class;
struct objc_class {
Class isa;
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
}
变量名和变量类型都很清楚的表示出了这个结构体里存放着什么。可以看到,Class本身也是一个OC对象,所属的类由isa指针指向,父类由super_class指针指向。而且类对象是singleton。
class类对象所属的类叫做metaclass,metaclass也有继承关系,文中有一张instance, class, metaclass之间指针指向关系的图,表示得非常清楚。图名叫做“NSObject metaclass是所有人的爸爸(误)”。
了解class的结构是为了理解introspection,也就是在runtime查询对象的类型。如果没有class之间的指向关系,类型查询没法顺藤摸瓜地进行。
类型查询常用两个方法:
- (BOOL)isMemberOfClass:(Class *)class
- (BOOL)isKindOfClass:(Class *)class
第一个方法用来判断对象是不是就属于这个class, 相当于 [object class] == [someClass class]
这样的直接判断。而第二个方法是判断对象是不是属于这个class或者它派生出来的class,也就是是不是在这个class派生出来的大树上。文中推荐尽量使用第二个方法。事实上在前面<Item 9: Use the Class Cluster Pattern to Hide Implementation Detail>一节也从另一个角度说明了采用第二种方法的好处。
<br />
Chapter 3. Interface and API Design
<br />
Item 15: Use Prefix Names to Avoid Namespace Clashes
<br />
这一篇说的是加前缀的重要性。
需要这么做的原因是OC没有namespace,namespace就是说命名是有可见范围的,就比如在PC上不同文件夹里放两个名字类型都一样的文件不会产生问题,因为各自的可见范围限于各自的文件夹。OC里没有提供这个便利,所以要特别注意避免重复的命名。
前缀的选择随意,推荐选择三个字母,因为系统框架的前缀采用的是两个字母。
需要添加前缀的有:类名,分类以及分类中的方法,文中还特别提醒了所用到的C函数,全局变量,以及引入的第三方库。
好了这一篇很短,主要就这些内容,看到这里我觉得我需要去改代码了_(:з」∠)_