iOS开发之基础篇(12)—— Category(分类)、Extension(扩展)

版本

Xcode 8.3.2

一、分类

Category这里称分类(另说类别、类目)。
分类的作用:扩展已有类(包括系统类)的功能。比如给NSString增加一个字符串反转的方法。你可能想到了,用继承也可以增加这个功能。那为什么又搞出个分类呢?不妨来对比一下。

1、继承(Inherit) VS 分类(Category)

  • 继承

    继承特点:
    1)子类可以扩展自己特有的属性(包括新增成员变量),但是不能访问父类的私有成员变量。
    2)子类可以扩展自己特有的方法,且新方法可以与原方法同名,而不会对父类原方法产生影响。

    什么情况下使用继承?
    1)扩展类的属性。
    2)新扩展的方法与原方法同名,但是还需要使用父类的实现。

  • 分类

    分类特点:
    1)分类可以在不改变类名和原有类的实现的前提下,进行类的扩展。
    2)在方法内部可以访问原有类的成员变量(在头文件中定义的成员变量),不能给原有类增加成员变量(使用@property只能生成setter和getter方法的声明,不能生成方法的实现,也不会生成带“_”的成员变量)。 可以给原有类添加属性:使用Runtime里的运行时关联对象方法来实现属性的setter和getter方法。
    3)分类与原有类不能有同名的方法。因为这样会覆盖原类的实现,而无法访问到原来的方法。
    4)分类之间也不能有同名的方法。因为最后编译的那个方法会覆盖其他方法。

    什么情况下使用分类?
    1)针对系统类(例如:NSString、NSArray、NSNumber等),系统本身不提倡使用继承去扩展方法,因为这些类内部实现对继承有所限制,所以应使用分类的方式扩展。
    2)分类支持开发人员针对自己构建的类,把相关的方法分组到多个单独的文件中,对于大型而复杂的类,这有助于提高可维护性,并简化单个源文件的管理。

2、给分类添加方法

步骤:

  • 为原有类创建一个分类
  • 在分类中增加新方法的声明和实现

按图:

1
2
3
4

注意到上图中的+Revers和(Revers),这些是固定格式,表过不提。

接下来贴上开头提到的字符串反转代码:

.h文件

#import <Foundation/Foundation.h>

@interface NSString (Revers)

// 字符串反转方法
-(NSString *)stringByReversString;

@end

.m文件

#import "NSString+Revers.h"

@implementation NSString (Revers)

// 字符串反转方法
-(NSString *)stringByReversString {

    NSUInteger length = [self length];
    NSMutableArray *array = [NSMutableArray arrayWithCapacity:length];

    for(long i=length-1; i>=0; i--){
        unichar c = [self characterAtIndex:i];
        [array addObject:[NSString stringWithFormat:@"%c",c]];
    }

    NSMutableString *str = [NSMutableString stringWithCapacity:length];
    for(int i=0; i<=length-1; i++){
        [str appendString:array[i]];
    }

    return str;
}

@end

main中调用:

#import <Foundation/Foundation.h>
#import "NSString+Revers.h"

int main(int argc, const char * argv[]) {

    NSString *str = @"abcde";
    str = [str stringByReversString];
    NSLog(@"%@",str);

    return 0;
}

输出如下:

3、给分类添加属性

前文提到,分类添加属性,系统只能生成setter和getter方法的声明,不能生成方法的实现,也不会生成带“_”的成员变量。
如果我们仍然想给分类添加可用的属性,那么可以使用Runtime里的运行时关联对象方法来实现属性的setter和getter方法。
关于Runtime的技术原理,请参考这里

实现步骤如下:

  • 1、.h文件中添加属性
#import <Foundation/Foundation.h>

@interface NSString (Revers)

@property (nonatomic, retain) NSNumber *tag;

@end
  • 2、导入Runtime头文件
    #import <objc/runtime.h>
  • 3、.m文件中实现属性的setter和getter方法
#import "NSString+Revers.h"
#import <objc/runtime.h>

static char tagKey;    // 用来标记是哪一个属性的key(静态变量地址唯一且不变)

@implementation NSString (Revers)

// tag的setter方法
- (void)setTag:(NSNumber *)tag {

    // 注意第四个参数参数OBJC_ASSOCIATION_RETAIN_NONATOMIC是和属性修饰符对应的
    objc_setAssociatedObject(self, &tagKey, tag, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

// tag的getter方法
- (NSNumber *)tag {
    
    return objc_getAssociatedObject(self, &tagKey);
}


@end
  • 4、调用属性示例
    NSString *str = @"abcde";   
    str.tag = @101;
    NSLog(@"tag:%@",str.tag);

注意
Xcode中对objc_setAssociatedObject的定义如下。

objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
                         id _Nullable value, objc_AssociationPolicy policy);

第三个参数(即要关联的数据类型)是id数据类型,而id类型是指向对象的指针,所以说,关联对象不能是基本数据类型(如BOOL、NSInteget、long等等)。当我们需要为分类添加基本数据类型的时候,可以先将基本数据类型转化成OC对象,再添加属性。

二、扩展

先来看看:

1
2
3

注意到上图中的_Exs和(),这些是扩展固定格式,是不是感觉和分类有点相似呢?
其实,扩展也叫匿名分类,区别是扩展只有.h文件,而实现是写在被扩展类的.m文件中的。因此,扩展的功能可以认为是被扩展类所私有的,其他类无法使用。
扩展的作用:为类增加私有的属性和方法。一般不用于给系统类添加方法。
因为是私有属性和方法,我们一般不另外生成Extension(扩展)的.h文件,而是将.h文件里的内容(声明)直接写在被扩展类的.m文件里。
例如,我们要扩展一个自定义的Person类,不用另外创建Extension的.h文件,而是在Person的.m文件里加入:

@interface Person () {
    //  添加成员变量
    NSString * _sex;
}
// 添加方法,实现要写在下方的@implementation里
- (void)sayHello;

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