Objective-C
Objective-C
语言的起源使用 #import "ClassName.h"
能够引入其余文件的全部接口细节。git
使用 @class ClassName
“向前声明”(forward declaring),只声明有这个类,没有具体细节,能够解决上述问题。github
除非确实有必要,不然不要引入头文件。通常来讲,应在某个类的头文件中使用向前声明来说起别的类,并在实现文件中引入那些类的头文件。这样作能够尽可能下降类之间的耦合(coupling)。objective-c
继承 和 听从协议 不能使用向前声明。 有时没法使用向前声明,好比要声明某个类遵循一项协议。这种状况下,尽可能把“该类遵循某协议”的这条声明移至“分类”中。若是不行的话,就把协议单独放在一个头文件中,而后将其引入。数组
向前声明的做用:缓存
- 防止引入根本用不到的内容,减小头文件细节引用。
- 解决两个类相互引用的问题。
将引入头文件的时机尽可能延后,只在确有须要时才引入,这样能够减小类的使用者所需引入头文件的数量。函数
使用字面量语法(literal syntax)能够缩减源代码的长度,使其更为易读。性能
#define
预处理指令#define
定义的常量没有类型信息,编译器只会在编译前据此执行查找与替换操做。即便有人从新定义了常量值,编译器也不会产生警告信息,这将致使应用程序中的常量值不一致。优化
// .h 文件 @interface 类名: 父类名 ... @end // .m 文件 // 类内使用 static const 类型 常量名 = 常量值; @implementation 类名 ... @end
// .h 文件 // 类外可用声明 extern 类名 const 常量名; @interface 类名: 父类名 ... @end // .m 文件 // 类外可用声明 类名 const 常量名 = 常量; @implementation 类名 ... @end
常量名称经常使用命名法是:编码
k
。switch
语句中不要实现 default
分支,便于加入新枚举后,编译器报错,知道须要修改的地方。==
操做符比较的是两个指针自己,不是其所指的对象。NSObject
协议中声明的 isEqual
方法判断两个对象的等同性。 isEqual
默认实现是:当且仅当其 “指针值” 彻底相等时,这两个对象才相等。NSString
: isEqualToString:
NSArray
: isEqualToArray:
NSDIctionary
: isEqualToDictionary:
若比较的对象不是对应的类型,就会抛出异常,崩溃。类族模式:使用继承,实现多种职能的子类,父类经过设定不一样的类型来建立某种子类,执行其相应的职能。 做用:将实现细节隐藏在一套简单的公共接口后面。 须要注意的是,建立的实例的真实类型是什么,须要咱们知道atom
新增 Cocoa
中 NSArray
这样的类族的子类,须要遵照如下几条规则:
NSArray
自己只是包在其余隐藏对象外面的壳,它仅仅定义了全部数组都需具有的一些接口。在对象中存放相关信息:
- 从对象所属的类中继承一个子类,而后修改这个子类对象。
- 经过“关联对象”的特性,给某对象关联许多其余对象,这些对象经过“键”来区分。
以给定的键和存储策略为某对象设置关联对象值
objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy)
参数说明:
object
关联的源对象。key
关联的 key
。一般使用静态全局变量作键。value
关联 key
所对应的值。传 nil
能够清除现有的关联。policy
关联的存储策略,也就是对应的内存管理语义,是一个枚举值。objc_AssociationPolicy
枚举值以下表: | 关联类型 | 等效的 @property 属性 | | --- | --- | OBJC_ASSOCIATION_ASSIGN | assign | OBJC_ASSOCIATION_RETAIN_NONATOMIC | nonatomic, retain | OBJC_ASSOCIATION_COPY_NONATOMIC | nonatomic, copy | OBJC_ASSOCIATION_RETAIN | retain | OBJC_ASSOCIATION_COPY | copy |根据给定的键从某对象中获取对应的关联对象值。
objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
移除指定对象的所有关联对象。
objc_removeAssociatedObjects(id _Nonnull object)
注:只有在其余方法都行不通时才考虑使用它。如果滥用,则很快就会令代码失控,使其难于调试。
objc_msgSend
的做用代码:
id returnValue = [someObject messageName: paramter];
代码说明:
someObject
接受者。messageName
选择子。选择子和参数合起来称为“消息”
底层C语言代码实现:
id returnValue = objc_msgSend(someObject, @selector(messageName:), paramter);
原型代码:
void objc_msgSend(id self, SEL cmd, ...)
这是个“参数个数可变函数”,能接受两个或者两个以上的参数。 参数说明:
self
接受者。cmd
选择子(方法的名字)。- 后续参数为消息中的那些参数,顺序不变。
具体实现:
注:OC
方法调用须要不少步骤,较为耗时。objc_msgSend
会将匹配结果缓存在“快速映射表”,每一个类都有这样一块缓存。虽然仍是不如“静态绑定的函数调用操做”那么迅速,可是也不会慢不少。
边界状况:
objc_msgSend_stret
待发送的消息要返回结构体,就交由此函数处理。objc_msgSend_fpret
待发送的消息要返回浮点数,就交由此函数处理。objc_msgSendSuper
要给超类发消息,就交由此函数处理。如:[super message:parameter]
。
Objective-C
对象的每一个方法均可以看作简单的 C
函数,其原型以下:
<return_type> Class_selector(id self, SEL _cmd, ...)
这个原型和 objc_msgSend
函数很像,是为了利用 “尾调用优化” 技术。令 “跳至方法实现” 这一操做跟简单些。
使用范围:某函数的最后一项操做仅仅是调用另外一个函数而不会将其返回值另做他用。 步骤:编译器会生成调转至另外一函数所需的指令码,不会向调用堆栈中推人新的 “栈帧”。 不优化后果:
Objective-C
方法以前,都须要为调用 objc_msgSend
函数准备 “栈帧”,能够在 “栈踪影” 中看到。消息转发: 第一阶段:动态方法解析: 征询接收者(所属的类),看其是否能动态添加方法,以处理当前这个 “未知的选择子”。 第二阶段: 1. 备援的接收者: 请接收者看看有没有其余对象(备援的接收者)能处理这条消息。 2. 完整的消息转发机制: 运行期系统会把与消息有关的所有细节都封装到
NSInvocation
对象中,再给接收者最后一次机会,令其设法解决当前还未处理的这条消息。
是否能新增一个实例方法来处理选择子,调用方法以下:
// 实例方法 + (BOOL)resolveInstanceMethod:(SEL)selector // 类方法 + (BOOL)resolveClassMethod:(SEL)selector
使用前提:相关方法的实现代码已经写好,只等运行的时候动态插入到类里面。
动态添加方法函数以下:
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)参数说明:
cls
添加方法的类name
被添加方法的名字imp
函数指针,指向待添加的方法(C语言实现)。type
待添加方法的 “类型编码” 。
是否有其余对象处理这条消息,调用方法以下:
- (id)forwardingTargetForSelector:(SEL)selector
若找到备援对象,该方法返回备援对象,反之,返回 nil
。
注意:咱们没法操做经由这一步所转发的消息。
建立 NSInvocation
对象,此对象包含 选择子 、目标 及 参数。
消息派发调用方法以下:
- (void)forwardInvocation:(NSInvocation *)invocation
若发现某调用操做不该由本类处理,则向上寻找,直至 NSObject
。若是最后调用了 NSObject
的方法,那么该方法还会继续调用 doesNotRecognizeSelector:
以抛出异常,代表选择子最终未能获得处理。
消息转发全流程以下图:
接收者在每一步中均有机会处理消息。步骤越日后,处理消息的代价就越大。
消息转发代码:https://github.com/AlonerOwl/Runtime/tree/master/Runtime/MessageSend
函数指针(IMP):id (*IMP)(id, SEL, ...)
方法表:函数指针所组成的一个集合。
操做类的方法表:
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)第12条已经说过了。
func method_exchangeImplementations(_ m1: Method, _ m2: Method)参数:两个待交换的方法实现 获取方法实现函数:
Method class_getInstanceMethod(Class cls, SEL name)
这个方法能够为那些 “彻底不知道具体实现” 的黑盒方法增长日志记录功能,这很是有助于程序调试。 如果滥用,反而会令代码变的不易读懂且难于维护。
method swizzling代码:https://github.com/AlonerOwl/Runtime/tree/master/Runtime/MethodSwizzling