第九条:以类族模式隐藏实现细节
类族可以隐藏抽象基类的实现细节,其中IOS中常用的UIButton就是使用的这种模式。
1
+ (UIButton *)buttonWithType:(UIButtonType)type;
在具体实现中,会根据
type
的不同,调用不同子类的初始化方法。在具体的应用中,也可以使用这种模式,通过一个通用的基类创建不同的子类对象。
声明枚举类型
1
2
3
4
5typdef NS_ENUM(NSUInteger, EOCEmployeeType) {
EOCEmployeeTypeDeveloper,
EOCEmployeeTypeDesigner,
EOCEmployeeTypeFinance
}声明工厂方法
1
+ (EOCEmployee *)employeeWithType:(EOCEmployeeType)type;
实现工厂方法
1
2
3
4
5
6
7
8
9
10
11
12
13+ (EOCEmployee *)employeeWithType:(EOCEmployeeType)type {
switch (type) {
case EOCEmployeeTypeDeveloper:
return [EOCEmployeeTypeDeveloper new];
break;
case EOCEmployeeTypeDesigner:
return [EOCEmployeeTypeDesigner new];
break;
case EOCEmployeeTypeFinance:
return [EOCEmployeeTypeFinance new];
break;
}
}
第十条:关联对象
关联对象可以在不改变类的实现或继承类的情况下,实现自己的功能
常用的关联方法有:
给某对象设置关联对象,通过键值的方法
1
void objc_setAssociatedObject (id object, void *key, id value, objc_AssociationPolicy policy)
获取制定key的获取关联对象值
1
id objc_getAssociatedObject(id object, void *key)
移除关联
1
void objc_removeAssociatedObjects(id object)s
objc_AssociationPolicy
含义
OBJC_ASSOCIATION_ASSIGN
对应assign
OBJC_ASSOCIATION_RETAIN_NONATOMIC
对应nonatomic, retain
OBJC_ASSOCIATION_COPY_NONATOMIC
对应nonatomic, copy
OBJC_ASSOCIATION_RETAIN
对应retain
OBJC_ASSOCIATION_COPY
对应copy
应用场景:
1
2
3
static void *EOCMyAlertViewKey = "EOCMyAlertViewKey";1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21- (void)askUserAQuestion {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"Question"
message :@"What do you want"
delegate:self
cancelButtonTittle:@"Cancel"
otherButtonTitle:@"continue",nil];
void (^block)(NSInteger) = ^(NSInteger buttonIndex) {
if (buttonIndex) == 0 {
[self doCancel];
}else {
[self doContinue];
}
};
objc_setAssociatedObject(alert,
EOCMyAlertViewKey,
block,
OBJC_ASSOCIATION_COPY)
[alert show];
}1
2
3
4// UIAlertViewDelegate
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
void (^block)(NSInteger) = objc_getAssociatedObject(alertView, EOCMyAlertViewKey); block(buttonIndex);
}
第十一条:理解objc_msgSend的作用
给对象发送消息可以这样写:
1
id returnValue = [someObject messageName:parameters];
转化成运行时语句:
1
id returnValue = objc_msgSend(someObject, @selector(messageName:),parameters);
- 此时objc_msgSend会检索方法列表,如果找到就跳转到实现代码,如果找不到就沿着继承关系继续向上查找,等找到合适的方法再跳转,如果一直追溯到最顶层的基类仍没找到方法,就会采用消息转发的方式。
- 当找到了方法的实现代码,就会放到每个类对应的缓存中。下次再调用相同的方法的时候就不用经历上面的过程,直接调用缓存中的方法实现就行了。
- 在特别的情况下会调用另一些方法:
objc_msgSend_stret
:返回结构体
objc_msgSend_fpret
:返回的是浮点数
objc_msgSendSuper
:直接调用父类方法
第十二条:理解消息转发机制
当对象在运行时接收到无法解读的消息后,会启动消息转发机制,在编写代码是可以通过加入消息转发机制,来处理未知消息。
消息转发可以分为两大阶段:动态添加方法
对象在收到无法解读的消息时,首先调用下列方法:1
2+ (BOOL)resolveInstanceMethod:(SEL)selector;
+ (BOOL)resolveClassMethod:(SEL)selector;例如下面的方法实现会通过选择字的名称生成对应的
setter
和getter
。1
2
3
4
5
6
7
8+ (BOOL)resolveInstanceMethod:(SEL)selector {
NSString *selectorString = NSStringFromSelector(selector);
if ([selectorString hasPrefix:@"set"]) {
class_addMethod(self, selector, (IMP)autoDictionarySetter, "v@:@");
}else {
class_addMethod(self, selector, (IMP)autoDictionaryGetter, "@@:");
}
}这种办法的前提是相关方法的实现代码已经写好,常用在
@dynamic属性
完整的消息转发机制
备援接收者
当通过动态添加方法失败后,运行时系统会尝试转发给其他的接收者来处理1
- (id)forwardingTargetForSelector:(SEL)selector;
该方法返回备选的接收对象,如果找不到就返回
nil
。通过此方案可以用组合模拟多继承,具体实现是,在一个对象内部可以有多个其他对象,这时可以通过消息转发获取到备援接收者,并向其发送消息。这样在使用时从外界看来就是本体对象处理了这些消息,造成多继承的假象。
但是要注意的一点是,此种方法不能修改信息内容,并且编译报错,要想解决编译错误就要新建一个
category
把要转发的消息声明在.h中,但并不需要实现,这样编译就不会报错了。完整的消息转发
调用下面方法实现转发
1
2
3
4
5
6- (void)forwardInvocation:(NSInvocation *)anInvocation {
if ([someOtherObject respondsToSelector:[anInvocation selector]])
[anInvocation invokeWithTarget:someOtherObject];
else
[super forwardInvocation:anInvocation];
}