类别、继承和类扩展

基础知识是很重要的,但往往容易被忽视,就个人而言出现的问题很多都是由于自身对基础知识的理解不充分导致的。所以,我们就OC中比较容易混淆的几个概念做详细的剖析,明确知识点。------前言

一、iOS中,各类添加方法有两种:继承和类别。

a、继承
面向对象三大特性之一(封装、继承、多态),子类会继承父类所有的方法和属性。
b、类别
类别也可以称为分类,是OC的特性,可以在不改变原有类的前提下,实现对类方法的扩展。类别是不能增加属性的,但是使用runtime可以做到。我们在前面介绍runtime的文章中有介绍到。

二、两者区别:

a、继承和类别最大的区别就是,一般情况下类别不能扩展属性,而继承可以扩展属性。
b、类别的方法比原类的方法的优先级高,如果方法相同,类别的方法会覆盖原类方法。

//.m文件内继承
#import "SDImageCache.h"
#import "SDWebImageDecoder.h"
#import "UIImage+MultiFormat.h"
#import <CommonCrypto/CommonDigest.h>

// See https://github.com/rs/SDWebImage/pull/1141 for discussion
@interface AutoPurgeCache : NSCache
@end
//设置净化子类,移除所有监听
@implementation AutoPurgeCache
- (id)init
{
    self = [super init];
    if (self) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeAllObjects) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
    }
    return self;
}
@end


@interface SDImageCache ()

@property (strong, nonatomic) NSCache *memCache;
@property (strong, nonatomic) NSString *diskCachePath;
@property (strong, nonatomic) NSMutableArray *customPaths;
@property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t ioQueue;

@end


@implementation SDImageCache {
    NSFileManager *_fileManager;
}

+ (SDImageCache *)sharedImageCache {
    static dispatch_once_t once;
    static id instance;
    dispatch_once(&once, ^{
        instance = [self new];
    });
    return instance;
}
········
@end

三、场景使用

继承使用场景:
a、当需要扩展方法与原方法重名时,并且需要调用父类的同名方法。
b、当需要扩展属性时。

类别使用场景:
a、针对系统的一些类进行扩展。如:NSString、NSArray、NSDictionary等。因为这些类内部实现对继承有所限制,所以最好使用分类的方式。
b、类别支持开发人员针对自己构建的类,把相关的方法分组到多个单独的文件中,针对大型且复杂的类,可以提高可维护性和可读性,并简化单个源文件的管理。


  • 知识扩展
    类别为什么不能添加成员变量?
    1、类别里面虽然可以添加添加 property,但是这些 properties 并不会自动生成 Ivar,也就是不会有@synthesize 的作用,dyld 加载的期间,这些 categories 会被加载并 patch 到相应的类中。这个过程是一个动态过程,Ivar 不能动态添加,因为表示 ObjC 类的结构体运行时并不能改变。
    2、根本原因:分类里面不能添加ivar是因为分类本身并不是一个真正的类,它并没有自己的ISA指针。类最开始生成了很多基本属性,比如IvarList,MethodList,分类只会将自己的method attach到主类,并不会影响到主类的IvarList。这就是为什么分类里面不能添加成员变量的原因。
    ivar是成员变量。

思考:那么添加的成员变量放到哪里去了呢?
其实runtime是生成一个AssociationsManager类用来管理分类的关联对象,让分类能正常使用成员变量,有点类似于前面说到的用字典去保存的原理。通过查看源码,我们可以知道AssociationsManager类有一个AssociationsHashMap属性,这个属性是相当于一个字典,用来存储对象-关联对象的,也就是它的key是我们关联对象时传的self,也就是这个Person分类,以这个为key,然后value是一个ObjectAssociationMap,ObjectAssociationMap对象也相当于是一个字典,这个字典的key是我们关联对象时传进去的那个key,value是ObjcAssociation,ObjcAssociation对象里面有两个属性_value和_policy,这两个就是我们关联对象的值和关联策略了。
简单说AssociationsHashMap存储的是项目中所有分类的关联对象,里面应该是长这样{Person分类: AssociationsHashMap,Student分类: AssociationsHashMap},我们项目中有几个分类有关联对象,那AssociationsHashMap里面就有多少个元素。而AssociationsHashMap里面存放的就是关联对象的key和ObjcAssociation,里面应该长这样{@selector(weight):@(weight),@selector(name):name},一个分类里面有多少个关联对象AssociationsHashMap里面就有多少个元素。最后ObjcAssociation就是保存着关联对象的值和关联策略了。ObjcAssociation{unitptr_t _policy= OBJC_ASSOCIATION_RETAIN_NONATOMIC; id _value=@(weight)}

链接:https://www.jianshu.com/p/841a02ca8468


四、类的扩展

a、定义:扩展是类别(分类)的一种特殊形式。
b、格式:

interface 主类类名()
     @end
     扩展通常定义在主类.m文件中,扩展中声明的方法直接在主类.m文件中实现。

c、注意事项:
扩展中可以声明实例变量,可以声明属性。因为扩展通常定义在主类的.m文件中,所以扩展声明的方法和属性通常是私有的。
d、类别和扩展的区别
1、类别不能声明实例变量,且类别是公开的,文件名为:主类名+分类名
2、扩展可以声明实例变量,扩展是私有的,文件名:主类名_扩展标识.h,在主类的.m文件中#import该头文件。示例如下:

在.h中定义扩展的类方法:
@interface NSData (Utils)
- (NSString *)detectImageSuffix;
@end

在.m中实现扩展方法:
@implementation NSData (Utils)
- (NSString *)detectImageSuffix
{
    uint8_t c;
    NSString *imageFormat = @"";
    [self getBytes:&c length:1];
    
    switch (c) {
        case 0xFF:
            imageFormat = @".jpg";
            break;
        case 0x89:
            imageFormat = @".png";
            break;
        case 0x47:
            imageFormat = @".gif";
            break;
        case 0x49:
        case 0x4D:
            imageFormat = @".tiff";
            break;
        case 0x42:
            imageFormat = @".bmp";
            break;
        default:
            break;
    }
    return imageFormat;
}
@end

思考

一、子类中方法的优先级为什么会高于主类(或父类)呢?
在这里先说下结论:在执行方法时,子类的优先级高于父类,分类的优先级高于主类。
首先,编译器将类别方法[obj name];转化为objc_msgSend(obj, @selector (name));,在objc_msgSend函数中。首先通过obj的isa指针找到obj对应的class。在Class中先去cache中 通过SEL查找对应函数method,若 cache中未找到。再去methodList中查找,若methodlist中未找到,则取superClass中查找。若能找到,则将method加 入到cache中,以方便下次查找,并通过method中的函数指针跳转到对应的函数中去执行。
上边的解释已经说得很清楚了,在查找name方法时,先从子类开始查找,若找到了就直接执行不再向父类查找,若没有找到,才会向父类中查找。

二、但是还有一个问题,分类的优先级为什么会高于主类呢?
我们知道,无论我们有没有主动引入 Category 的头文件,Category 中的方法都会被添加进主类中。我们可以通过- performSelector: 等方式对 Category 中的相应方法进行调用,之所以需要在调用的地方引入 Category 的头文件,只是为了"照顾"编译器的感受。
在函数中对 Category 做了如下处理:
1.将 Category 和它的主类(或元类)注册到哈希表中;
2.如果主类(或元类)已实现,那么重建它的方法列表。

类中的旧有方法和 Category 中新添加的方法整合成一个新的方法列表(也就是重建了方法列表),并赋值给method_lists 或 method_list。通过探究这个处理过程,我们也印证了一个结论,那就是主类中的方法和 Category 中的方法在 runtime 看来并没有区别,它们是被同等对待的,都保存在主类的方法列表中

链接:https://www.jianshu.com/p/4cd699a66f9b

参考资料:http://blog.csdn.net/lvxiangan/article/details/44600947
http://blog.csdn.net/LVXIANGAN/article/details/76510870
http://blog.csdn.net/fiona_yang123456/article/details/41044979

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