分类(category)和类扩展(extension)

一、分类(类别):

背景:

在大型项目,企业级开发中多人同时维护同一个类,此时程序员A因为某项需求只想给当前类currentClass添加一个方法newMethod,那该怎么办呢?

最简单粗暴的方式是把newMethod添加到currentClass中,然后直接实现该方法就OK了。

但考虑到OC是单继承的,子类可以拥有父类的方法和属性。

如果把newMethod写到currentClass中,那么currentClass的子类也会拥有newMethod。但真正的需求是只需要currentClass拥有newMethod,而currentClass的子类不会拥有。

苹果为了解决这个问题,就引入了分类(Category)的概念。

分类运用场景举例:

举例:想要收集每个页面的启动时间。

问题1:

项目中已经有上百个页面了,如果一个一个的加,浪费时间不说,以后增加了新页面,还需要添加方法。

解决方法:

我们可以发现页面都继承了UIViewController,想要在每个页面都执行的代码,可以写在这些页面的父类中。我们可以把代码写在UIViewController中;出现问题2。

问题2:

UIViewController是官方类,我们只能调用期接口,并不能修改他的实现。

解决方法:使用分类(category)。

问题3:

本类是系统的类,这里是UIViewController,我们可以使用分类扩展他的方法,也可以重写他的方法,可是我需要在调用的地方加头文件,所有子类都写头文件和直接在子类写方法没有什么区别,怎么样可以使得不写头文件,子类就能调用我们写的代码呢?

回答:

我们可以进行方法交换(这样可以不必在调用的地方增加头文件),从而使得在实现的时候调用重写的方法。

概念

分类(Category)是OC中的特有语法,它是表示一个指向分类的结构体的指针。原则上它只能增加方法,不能增加成员(实例)变量。

作用

1.可以在不修改原来类的基础上,为一个类扩展方法。

2最主要的用法:给系统自带的类扩展方法。

注意

1.分类中只能添加“方法”,不能增加成员变量,因为分类的结构体指针中,没有属性列表,只有方法列表;

如果分类中声明了一个属性,那么分类只会生成这个属性的set、get方法声明,也就是不会有实现(调用set方法给该属性赋值会崩溃,调用get方法获取属性值也会崩溃);

原则上讲它只能添加方法,不能添加属性即成员变量,实际上可以通过其他方式(通过getter,setter手段即运行时进行添加)添加属性;

2.分类中的可以写@property, 但不会生成setter/getter方法, 也不会生成实现以及私有的成员变量(编译时会报警告);

3.分类中可以访问原来类中的成员变量,但是只能访问@protect和@public形式的变量。如果想要访问本类中的私有变量,分类和子类一样,只能通过方法来访问;

4.如果分类中有和原有类同名的方法, 会优先调用分类中的方法, 就是说会忽略原有类的方法。所以同名方法调用的优先级为分类 > 本类 > 父类。因此在开发中尽量不要覆盖原有类;

5.如果多个分类中都有和原有类中同名的方法, 那么调用该方法的时候执行谁由编译器决定;编译器会执行最后一个参与编译的分类中的方法。

1.分类不能添加属性的实质原因:

我们知道在一个类中用@property声明属性,编译器会自动帮我们生成_成员变量和setter/getter,但分类的指针结构体中,根本没有属性列表。所以在分类中用@property声明属性,既无法生成_成员变量也无法生成setter/getter。 因此结论是:我们可以用@property声明属性,编译和运行都会通过,只要不使用程序也不会崩溃。但如果调用了_成员变量和setter/getter方法,报错就在所难免了。

如果调用了,报错如下:

//普通声明,不会自动生成setter/getter,

//  programmer.nameWithoutSetterGetter = @"无setter/getter"; //调用setter,编译成功,运行报错为:(-[Programmer setNameWithSetterGetter:]: unrecognized selector sent to instance 0x7f9de358fd70')

//  NSLog(@"%@",programmer.nameWithoutSetterGetter);          //调用getter,编译成功,运行报错为-[Programmer setNameWithSetterGetter:]: unrecognized selector sent to instance 0x7fe22be11ea0'

//  NSLog(@"%@",_nameWithoutSetterGetter);        //这是调用_成员变量,错误提示为:(Use of undeclared identifier'_nameWithoutSetterGetter')

2.报错的根本原因是使用了系统没有生成的setter/getter方法,我们可以手动添加setter/getter来避免崩溃,完成调用;

由于OC是动态语言,方法真正的实现是通过runtime完成的,虽然系统不给我们生成setter/getter,但我们可以通过runtime手动添加setter/getter方法。

通过运行时手动添加:

#import

static NSString *nameWithSetterGetterKey = @"nameWithSetterGetterKey";//定义一个key值

@implementation Programmer (Category) 

//运行时实现setter方法

- (void)setNameWithSetterGetter:(NSString*)nameWithSetterGetter

 { 

 objc_setAssociatedObject(self, &nameWithSetterGetterKey, nameWithSetterGetter, OBJC_ASSOCIATION_COPY);

}

//运行时实现getter方法

- (NSString*)nameWithSetterGetter

 {

returnobjc_getAssociatedObject(self, &nameWithSetterGetterKey);

}

@end

运行:

//通过runtime实现了setter/getter

programmer.nameWithSetterGetter =@"有setter/getter";//调用setter,成功NSLog(@"%@",programmer.nameWithSetterGetter);//调用getter,成功 NSLog(@"%@",_nameWithSetterGetter); //这是调用_成员变量,错误提示为:(Use of undeclared identifier '_nameWithSetterGetter')

但是注意,以上代码仅仅是手动实现了setter/getter方法,但调用_成员变量依然报错。

二、类扩展:

Extension是Category的一个特例。类扩展与分类相比只少了分类的名称,所以称之为“匿名分类”。

作用:

能为某个类附加额外的属性,成员变量,方法声明;

一般的类扩展写到.m文件中;

一般的私有属性写到类扩展;

三、分类和类扩展的区别:

1.分类格式:

@interface待扩展的类(分类的名称)

@end

@implementation待扩展的名称(分类的名称)

@end

类扩展格式:

@interfaceXXX()

//私有属性

//私有方法(如果不实现,编译时会报警,Method definition for 'XXX' not found)

@end

2.分类原则上只能添加“方法”,不能增加成员变量,但是可以通过runtime解决无setter/getter的问题,进而添加属性;类扩展不仅可以增加方法,还可以增加实例变量(或者属性),只是该实例变量默认是@private类型的;

3.类扩展中添加的新方法,一定要实现,没被实现,编译器会报警;categorygory中没有这种限制,类别中的方法没被实现编译器是不会有任何警告的。这是因为类扩展是在编译阶段被添加到类中,而类别是在运行时添加到类中。

4.通常来讲,分类定义在.h文件中,但也可以定义.m文件中,此时分类的方法就变成私有方法;

类扩展可以定义在.m文件中,这种扩展方式中定义的变量都是私有的,也可以定义在.h文件中,这样定义的代码就是共有的,类扩展在.m文件中声明私有方法是非常好的方式。

5.类扩展不能像类别那样拥有独立的实现部分(@implementation部分),也就是说,类扩展所声明的方法必须依托对应类的实现部分来实现。

四、关于继承和分类的使用:

以下情况,使用继承:

1)新扩展的方法与原方法同名,但是还需要使用父类的实现。

2)扩展类的属性

以下情况,使用类别:

1)针对系统特定类,例如:NSString,NSArray,NSNumber等。

2)针对自定义类,对于大型而复杂的类,为提高可维护性,把相关的方法分组到多个单独的文件中。


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

推荐阅读更多精彩内容