OC如何实现多重继承

当单继承不够用,很难为问题域建模时,我们通常都会直接想到多继承。多继承是从多余一个直接基类派生类的能力,可以更加直接地为应用程序建模。但是Objective-C不支持多继承,由于消息机制名字查找发生在运行时而非编译时,很难解决多个基类可能导致的二义性问题。不过其实 Objective-C 也无需支持多继承,我们可以找到如下几种间接实现多继承目的的方法:

**消息转发 **
**delegate和protocol **
**类别 **

消息转发

当向someObject发送某消息,但runtime system在当前类和父类中都找不到对应方法的实现时,runtime system并不会立即报错使程序崩溃,而是依次执行下列步骤:


分别简述一下流程:

1.动态方法解析:向当前类发送 resolveInstanceMethod: 信号,检查是否动态向该类添加了方法。(迷茫请搜索:@dynamic)
2.快速消息转发:检查该类是否实现了 forwardingTargetForSelector: 方法,若实现了则调用这个方法。若该方法返回值对象非nil或非self,则向该返回对象重新发送消息。
3.标准消息转发:runtime发送methodSignatureForSelector:消息获取Selector对应的方法签名。返回值非空则通过forwardInvocation:转发消息,返回值为空则向当前对象发送doesNotRecognizeSelector:消息,程序崩溃退出。

顾名思义,我们可以利用上述过程中的2、3两种方式来完成消息转发。

快速消息转发

快速消息转发的实现方法很简单,只需要重写 - (id)forwardingTargetForSelector:(SEL)aSelector 方法即可。
我来举个简单的例子,比如现有2个类:Teacher 和 Doctor,Doctor可以做手术(operate方法)。

@interface Teacher : NSObject   
  
@end   
@interface Doctor : NSObject   
   
- (void)operate;   
@end   

通过快速消息转发,可以很轻松的让teacher调用doctor的方法做手术。

Teacher类需要实现将消息转发给Doctor:

- (id)forwardingTargetForSelector:(SEL)aSelector   
{   
   Doctor *doctor = [[Doctor alloc]init];   
   if ([doctor respondsToSelector:aSelector]) {   
       return doctor;   
   }   
   return nil;   
}   

虽然消息可以动态转发传递,但是编辑器的静态检查是绕不过的,那么问题来了,既然Teacher类没有实现operate方法又该如何声明呢?
到目前为止,我只想到下面2种方法:

声明方法1 ———— 类别

@interface Teacher (DoctorMethod)   
- (void)operate;   
  
@end   

声明方法2 ———— 导入头文件、调用时强转类型

Teacher类头文件需要包含Doctor头文件,告诉编译器去Doctor.h中可以找到operator方法的声明,并且在调用时强转类型。

Teacher *teacher = [[Teacher alloc]init];   
[(Doctor *)teacher operate];   

有兴趣可以思考一个问题:如果将其类型转成 id ,也可以编译通过,并实现转发。可是会带来什么隐患呢?

方法1使用类别足够清晰简便,为什么还要提出办法2呢 ? 我的想法是,方法1的弊端是抛出来的方法是定死的,而且在.h里露着;方法2就相对灵活,而且隐藏了我要转发的消息。

标准消息转发
标准消息转发需要重写 methodSignatureForSelector: 和 forwardInvocation: 两个方法即可。
发流程如图所示:


转发重写方法:

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector   
{   
    NSMethodSignature* signature = [super methodSignatureForSelector:aSelector];   
    if (signature==nil) {   
        signature = [someObj methodSignatureForSelector:aSelector];   
    }   
    NSUInteger argCount = [signature numberOfArguments];   
    for (NSInteger i=0 ; i

    }   
       
    return signature;   
}   
 
- (void)forwardInvocation:(NSInvocation *)anInvocation   
{   
    SEL seletor = [anInvocation selector];   
    if ([someObj respondsToSelector:seletor]) {   
        [anInvocation invokeWithTarget:someObj];   
    }   
       
}   

两种消息转发方式的比较

快速消息转发:简单、快速、但仅能转发给一个对象。
标准消息转发:稍复杂、较慢、但转发操作实现可控,可以实现多对象转发。

delegate和protocol

委托是Objective-C中最常用的一种回调机制。用法我觉得没什么好说的,总结一下该机制特点:
委托协助主体完成操作任务,将需要定制化的操作预留给委托对象来自定义实现,类似子类化主体。
除此之外,可以用作事件监听。
一时还真想不出来了…

类别
个人认为类别是Objective-C设计的一大精髓,也是我爱上Objective-C的最大理由。

类别是个强大的东西,它既可以为类添加方法,也可以添加实例。一定有不少人不认同,想提醒我:类别的局限性之一就是无法向类中添加新的实例变量。 背书真心毁人,听我举个例子慢慢说。

重新再来个Teacher类:

@interface Teacher : NSObject   
{   
    NSUInteger age;   
}   
   
@end   

光有个年龄还不能满足对teacher的描述,我想加个profession实例来存teacher的专业。直观的想法是子类化Teacher,其实也可以用类别。

你需要了解一下 runtime 编程知识,关注一下 objc_setAssociatedObject 和 objc_getAssociatedObject 。

//   
//  Teacher+Profession.m   
//     
  
#import "Teacher+Profession.h"   
#import   

  
const char *ProfessionType = "NSString *";   
@implementation Teacher (Profession)   
  
-(void)setProf:(NSString*)prof   
{   
   objc_setAssociatedObject(self, ProfessionType, prof, OBJC_ASSOCIATION_RETAIN_NONATOMIC);   
}   
  
-(NSString *)prof   
{   
   NSString *pro = objc_getAssociatedObject(self, ProfessionType);   
   return pro;   
}   
  
@end   

现在就可以通过setProf: 和 prof 来存取 teacher 的 profession 值了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容

  • 当单继承不够用,很难为问题域建模时,我们通常都会直接想到多继承。多继承是从多余一个直接基类派生类的能力,可以更加直...
    狼凤皇阅读 329评论 1 1
  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,690评论 0 9
  • Objective-C Objective-C是对C的扩充,它最初的设计目的是给C语言一个通过简单且直观的方式而实...
    ValienZh阅读 5,094评论 0 7
  • 1、请简述什么是主键、什么是外键 主键: 一张表(关系)的一个列(睡醒)或多个列可以作为主键,但是前提是让这个列作...
    陪你看日出去阅读 198评论 0 0
  • 本文转载自:http://yulingtianxia.com/blog/2014/11/05/objective-...
    ant_flex阅读 751评论 0 1