当前位置:K88软件开发文章中心编程语言LinuxLinux01 → 文章内容

iOS开发——Category在项目中的实际运用

减小字体 增大字体 作者:佚名  来源:网上搜集  发布时间:2019-1-4 9:00:55

-->

郑秀妍

写在前面

先看两行代码:
1.

1
label2<span class="hljs-variable">.textColor</span> = [<span class="hljs-built_in">UIColor</span> colorWithHexString:<span class="hljs-string">@"707070"</span>];

2.

1
2
3
4
5
_table.header = [MJRefreshHeader headerWithRefreshingBlock:^{
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (<span class="hljs-keyword">int64_t</span>)(<span class="hljs-number">2.0</span> * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
               <span class="hljs-comment">// 刷新时的相关处理</span>
            });
        }];

相信大家对上面的两行代码都不会陌生

  • 上一行:UIColor原本是没有读取十六进制颜色值的方法的
  • 下一行:UITableView原本是没有header属性的

那么,How it happened?😳

Because of the Category!😌


Category(类别)简介

  1. 利用Objective-C的动态运行时分配机制,可以为现有的类(自己的或系统的或三方库的)添加新方法,这种为现有的类添加新方法的方式称为类别category,他可以为任何类添加新的方法,包括那些没有源代码的类。
  2. 类别使得无需创建对象类的子类就能完成同样的工作。

在这里新建Category

Category的作用

一. 扩展类的方法
这是我们用的最多的,现在就以扩展

1
UIColor

的一个读取16进制颜色的方法为例:

先建一个UIColor的Category

新建的Category文件

在.m文件中编写扩展的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
+ (<span class="hljs-built_in">UIColor</span> *)colorWithHexString:(<span class="hljs-built_in">NSString</span> *)color alpha:(<span class="hljs-built_in">CGFloat</span>)alpha
{
    <span class="hljs-comment">//删除字符串中的空格</span>
    <span class="hljs-built_in">NSString</span> *cString = [[color stringByTrimmingCharactersInSet:[<span class="hljs-built_in">NSCharacterSet</span> whitespaceAndNewlineCharacterSet]] uppercaseString];
    <span class="hljs-comment">// String should be 6 or 8 characters</span>
    <span class="hljs-keyword">if</span> ([cString length] < <span class="hljs-number">6</span>)
    {
        <span class="hljs-keyword">return</span> [<span class="hljs-built_in">UIColor</span> clearColor];
    }
    <span class="hljs-comment">// strip 0X if it appears</span>
    <span class="hljs-comment">//如果是0x开头的,那么截取字符串,字符串从索引为2的位置开始,一直到末尾</span>
    <span class="hljs-keyword">if</span> ([cString hasPrefix:<span class="hljs-string">@"0X"</span>])
    {
        cString = [cString substringFromIndex:<span class="hljs-number">2</span>];
    }
    <span class="hljs-comment">//如果是#开头的,那么截取字符串,字符串从索引为1的位置开始,一直到末尾</span>
    <span class="hljs-keyword">if</span> ([cString hasPrefix:<span class="hljs-string">@"#"</span>])
    {
        cString = [cString substringFromIndex:<span class="hljs-number">1</span>];
    }
    <span class="hljs-keyword">if</span> ([cString length] != <span class="hljs-number">6</span>)
    {
        <span class="hljs-keyword">return</span> [<span class="hljs-built_in">UIColor</span> clearColor];
    }

    <span class="hljs-comment">// Separate into r, g, b substrings</span>
    <span class="hljs-built_in">NSRange</span> range;
    range<span class="hljs-variable">.location</span> = <span class="hljs-number">0</span>;
    range<span class="hljs-variable">.length</span> = <span class="hljs-number">2</span>;
    <span class="hljs-comment">//r</span>
    <span class="hljs-built_in">NSString</span> *rString = [cString substringWithRange:range];
    <span class="hljs-comment">//g</span>
    range<span class="hljs-variable">.location</span> = <span class="hljs-number">2</span>;
    <span class="hljs-built_in">NSString</span> *gString = [cString substringWithRange:range];
    <span class="hljs-comment">//b</span>
    range<span class="hljs-variable">.location</span> = <span class="hljs-number">4</span>;
    <span class="hljs-built_in">NSString</span> *bString = [cString substringWithRange:range];

    <span class="hljs-comment">// Scan values</span>
    <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> r, g, b;
    [[<span class="hljs-built_in">NSScanner</span> scannerWithString:rString] scanHexInt:&r];
    [[<span class="hljs-built_in">NSScanner</span> scannerWithString:gString] scanHexInt:&g];
    [[<span class="hljs-built_in">NSScanner</span> scannerWithString:bString] scanHexInt:&b];
    <span class="hljs-keyword">return</span> [<span class="hljs-built_in">UIColor</span> colorWithRed:((<span class="hljs-keyword">float</span>)r / <span class="hljs-number">255.0</span>f) green:((<span class="hljs-keyword">float</span>)g / <span class="hljs-number">255.0</span>f) blue:((<span class="hljs-keyword">float</span>)b / <span class="hljs-number">255.0</span>f) alpha:alpha];
}

在.h文件中将方法暴露出来

1
2
<span class="hljs-comment">/* 从十六进制字符串获取颜色 */</span>
+ (<span class="hljs-built_in">UIColor</span> *)colorWithHexString:(<span class="hljs-built_in">NSString</span> *)color alpha:(<span class="hljs-built_in">CGFloat</span>)alpha;

至此,扩展系统类UIColor的方法成功,需要时,导入头文件直接使用即可:

1
<span class="hljs-keyword">self</span><span class="hljs-variable">.view</span><span class="hljs-variable">.backgroundColor</span>=[<span class="hljs-built_in">UIColor</span> colorWithHexString:<span class="hljs-string">@"f7f7f9"</span>];

二. 扩展类的属性(结合runtime)
这个也是相当实用的,举个🌰:我们如果要给所有UIButton都添加一个name属性,怎么破?这个时候Category又可以秀一下了(都是套路):
.h文件里定义并暴露属性

1
2
<span class="hljs-comment">/** button的name */</span>
<span class="hljs-keyword">@property</span> (<span class="hljs-keyword">nonatomic</span>,<span class="hljs-keyword">copy</span>) <span class="hljs-built_in">NSString</span> *name;

.m文件先导入

1
<objc/runtime.h>

,然后处理set和get方法

1
2
3
4
5
6
7
8
9
<span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> *strKey = &strKey;

- (<span class="hljs-keyword">void</span>)setName:(<span class="hljs-built_in">NSString</span> *)name{
    objc_setAssociatedObject(<span class="hljs-keyword">self</span>, & strKey, name, OBJC_ASSO<span class="hljs-built_in">CIATION_COPY</span>);
}

- (<span class="hljs-built_in">NSString</span> *)name{
    <span class="hljs-keyword">return</span> objc_getAssociatedObject(<span class="hljs-keyword">self</span>, &strKey);
}

然后就可以使用了😄

1
2
button<span class="hljs-variable">.name</span> = <span class="hljs-string">@"小button"</span>;
<span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"%@"</span>,button<span class="hljs-variable">.name</span>);

三. 在没有源代码的情况下可以用来修复BUG

当知道已有类中某个方法有BUG,但是这个类是以库的形式存在的,我们无法直接修改源代码的时候,Category也可以用于替代这个已有类中某个方法的实体,从而达到修复BUG的目的。

说白了就是替换原有的方法,不过替换之前要先想好是否会影响原有的功能以及是否有更深层的bug在后面等着你。。。😱

四. 整体替换
one day,产品跑来告诉我:系统的弹窗太丑了,要全部换成自定义弹窗。我怀着忐忑的心情在工程中输入

1
UIAlertView

进行搜索,尼玛,300多个搜索结果。。。当时我首先想到的是新建一个类,让它有和

1
UIAlertView

有相同的方法,然后将UIAlertView全部换成新类即可。不过后来仔细想想,方法虽行,但是要替换的太多了,万一出现什么差错就麻烦了。于是乎,我使用了Category,重置了

1
UIAlertView

的相关方法,仅仅新建了一个Category文件,没有在原来的工程中进行任何修改和替换。这里,应该有一波666😄

如何优雅的使用Category?

关于这一点,你首先得清楚Category和继承的区别:

继承Inherit
这个是面向对象语言都有的一个特性,子类会继承父类的方法和属性。
继承会新建一个子类,但类别不会。

弄清Category和继承的区别后,才知道什么时候用Category,什么时候用继承,这个也是面试官喜欢问的,所以不管怎样请务必融会贯通。
有人说:

如果需要添加一个新的变量,则需添加子类。
如果只是添加一个新的方法,用Category是比较好的选择。

关于什么时候用Category,什么时候用继承,我个人经验如下:

  • 以UIButon为例,如果你希望你项目中所有的UIButton都有一个属性:name,那么这个时候直接用Category给UIButton扩展个属性即可。
  • 同样需要给button添加一个name属性,但是这种button只在某个或几个(反正就是用的情况不多)页面使用时,这时我通常采取继承的方式自定义一个button。

项目实际运用举例

先看一张UI设计图:

美工给的UI设计图

项目中会大量使用到带红色边框的button,如果我们每次都来实现这个边框效果必然非常繁琐。这个时候用Category给UIButton扩展一个自定义构造方法便可完美解决这个烦恼😁
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<span class="hljs-comment">/**
 *  自定义带边框的button
 *
 *  @param frame       frame
 *  @param borderColor 边框颜色
 *  @param borderWidth 边框宽度
 *
 *  @return 自定义带边框的button
 */</span>
- (instancetype)initWithFrame:(<span class="hljs-built_in">CGRect</span>)frame borderColor:(<span class="hljs-built_in">UIColor</span> *)borderColor borderWidth:(<span class="hljs-built_in">CGFloat</span>)borderWidth{
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">self</span> = [<span class="hljs-keyword">super</span> initWithFrame:frame]) {
        <span class="hljs-comment">// 边框颜色</span>
        <span class="hljs-keyword">self</span><span class="hljs-variable">.layer</span><span class="hljs-variable">.borderColor</span> = borderColor<span class="hljs-variable">.CGColor</span>;
        <span class="hljs-comment">// 边框宽度</span>
        <span class="hljs-keyword">self</span><span class="hljs-variable">.layer</span><span class="hljs-variable">.borderWidth</span> = borderWidth;
        <span class="hljs-comment">// title颜色(与边框颜色一致)</span>
        [<span class="hljs-keyword">self</span> setTitleColor:borderColor forState:<span class="hljs-built_in">UIControlStateNormal</span>];
        <span class="hljs-comment">// 圆角</span>
        <span class="hljs-keyword">self</span><span class="hljs-variable">.layer</span><span class="hljs-variable">.cornerRadius</span> = <span class="hljs-number">3</span>;
        <span class="hljs-comment">// 字号</span>
        [<span class="hljs-keyword">self</span><span class="hljs-variable">.titleLabel</span> setFont:[<span class="hljs-built_in">UIFont</span> systemFontOfSize:<span class="hljs-number">13</span>]];
    }
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span>;
}

使用:

1
2
3
<span class="hljs-built_in">UIButton</span> *button = [[<span class="hljs-built_in">UIButton</span> alloc]initWithFrame:<span class="hljs-built_in">CGRectMake</span>(<span class="hljs-number">100</span>, <span class="hljs-number">200</span>, <span class="hljs-number">69</span>, <span class="hljs-number">21</span>) borderColor:[<span class="hljs-built_in">UIColor</span> colorWithHexString:<span class="hljs-string">@"ff0000"</span>] borderWidth:<span class="hljs-number">2</span>];
    [button setTitle:<span class="hljs-string">@"立即购买"</span> forState:<span class="hljs-built_in">UIControlStateNormal</span>];
    [<span class="hljs-keyword">self</span><span class="hljs-variable">.view</span> addSubview:button];

效果图:

新构造的button

注意

  1. 如果category扩展的方法名已存在,会覆盖原有的方法名相同,是根据buildPhases->Compile Sources里面的从上至下顺序编译的,即后编译的会被调用。
  2. 新扩展的方法与原方法同名,但是还需要使用父类的实现时,这个时候只能用继承。

分享个我常用的Category

.h 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<span class="hljs-class"><span class="hljs-keyword">@interface</span> <span class="hljs-title">UIView</span> (<span class="hljs-title">frameAdjust</span>)</span>

<span class="hljs-comment">// 左上角x坐标</span>
- (<span class="hljs-built_in">CGFloat</span>)x;
- (<span class="hljs-keyword">void</span>)setX:(<span class="hljs-built_in">CGFloat</span>)x;

<span class="hljs-comment">// 左上角y坐标</span>
- (<span class="hljs-built_in">CGFloat</span>)y;
- (<span class="hljs-keyword">void</span>)setY:(<span class="hljs-built_in">CGFloat</span>)y;

<span class="hljs-comment">// 宽</span>
- (<span class="hljs-built_in">CGFloat</span>)width;
- (<span class="hljs-keyword">void</span>)setWidth:(<span class="hljs-built_in">CGFloat</span>)width;

<span class="hljs-comment">// 高</span>
- (<span class="hljs-built_in">CGFloat</span>)height;
- (<span class="hljs-keyword">void</span>)setHeight:(<span class="hljs-built_in">CGFloat</span>)height;

<span class="hljs-comment">// 中心点x</span>
- (<span class="hljs-built_in">CGFloat</span>)centerX;
- (<span class="hljs-keyword">void</span>)setCenterX:(<span class="hljs-built_in">CGFloat</span>)x;

<span class="hljs-comment">// 中心点y</span>
- (<span class="hljs-built_in">CGFloat</span>)centerY;
- (<span class="hljs-keyword">void</span>)setCenterY:(<span class="hljs-built_in">CGFloat</span>)y;

<span class="hljs-comment">/** 获取最大x */</span>
- (<span class="hljs-built_in">CGFloat</span>)maxX;
<span class="hljs-comment">/** 获取最小x */</span>
- (<span class="hljs-built_in">CGFloat</span>)minX;

<span class="hljs-comment">/** 获取最大y */</span>
- (<span class="hljs-built_in">CGFloat</span>)maxY;
<span class="hljs-comment">/** 获取最小y */</span>
- (<span class="hljs-built_in">CGFloat</span>)minY;

<span class="hljs-keyword">@end</span>

.m 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<span class="hljs-class"><span class="hljs-keyword">@implementation</span> <span class="hljs-title">UIView</span> (<span class="hljs-title">frameAdjust</span>)</span>

- (<span class="hljs-built_in">CGFloat</span>)x{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span><span class="hljs-variable">.frame</span><span class="hljs-variable">.origin</span><span class="hljs-variable">.x</span>;
}

- (<span class="hljs-keyword">void</span>)setX:(<span class="hljs-built_in">CGFloat</span>)x{
    <span class="hljs-keyword">self</span><span class="hljs-variable">.frame</span> = <span class="hljs-built_in">CGRectMake</span>(x, <span class="hljs-keyword">self</span><span class="hljs-variable">.y</span>, <span class="hljs-keyword">self</span><span class="hljs-variable">.width</span>, <span class="hljs-keyword">self</span><span class="hljs-variable">.height</span>);
}

- (<span class="hljs-built_in">CGFloat</span>)y{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span><span class="hljs-variable">.frame</span><span class="hljs-variable">.origin</span><span class="hljs-variable">.y</span>;
}

- (<span class="hljs-keyword">void</span>)setY:(<span class="hljs-built_in">CGFloat</span>)y{
    <span class="hljs-keyword">self</span><span class="hljs-variable">.frame</span> = <span class="hljs-built_in">CGRectMake</span>(<span class="hljs-keyword">self</span><span class="hljs-variable">.x</span>, y, <span class="hljs-keyword">self</span><span class="hljs-variable">.width</span>, <span class="hljs-keyword">self</span><span class="hljs-variable">.height</span>);
}

- (<span class="hljs-built_in">CGFloat</span>)width{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span><span class="hljs-variable">.frame</span><span class="hljs-variable">.size</span><span class="hljs-variable">.width</span>;
}

- (<span class="hljs-keyword">void</span>)setWidth:(<span class="hljs-built_in">CGFloat</span>)width{
    <span class="hljs-keyword">self</span><span class="hljs-variable">.frame</span> = <span class="hljs-built_in">CGRectMake</span>(<span class="hljs-keyword">self</span><span class="hljs-variable">.x</span>, <span class="hljs-keyword">self</span><span class="hljs-variable">.y</span>, width, <span class="hljs-keyword">self</span><span class="hljs-variable">.height</span>);
}

- (<span class="hljs-built_in">CGFloat</span>)height{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span><span class="hljs-variable">.frame</span><span class="hljs-variable">.size</span><span class="hljs-variable">.height</span>;
}

- (<span class="hljs-keyword">void</span>)setHeight:(<span class="hljs-built_in">CGFloat</span>)height{
    <span class="hljs-keyword">self</span><span class="hljs-variable">.frame</span> = <span class="hljs-built_in">CGRectMake</span>(<span class="hljs-keyword">self</span><span class="hljs-variable">.x</span>, <span class="hljs-keyword">self</span><span class="hljs-variable">.y</span>, <span class="hljs-keyword">self</span><span class="hljs-variable">.width</span>, <span class="hljs-keyword">self</span><span class="hljs-variable">.height</span>);
}

- (<span class="hljs-built_in">CGFloat</span>)centerX{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span><span class="hljs-variable">.center</span><span class="hljs-variable">.x</span>;
}

- (<span class="hljs-keyword">void</span>)setCenterX:(<span class="hljs-built_in">CGFloat</span>)x{
    <span class="hljs-keyword">self</span><span class="hljs-variable">.center</span> = <span class="hljs-built_in">CGPointMake</span>(x, <span class="hljs-keyword">self</span><span class="hljs-variable">.center</span><span class="hljs-variable">.y</span>);
}

- (<span class="hljs-built_in">CGFloat</span>)centerY{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span><span class="hljs-variable">.center</span><span class="hljs-variable">.y</span>;
}

- (<span class="hljs-keyword">void</span>)setCenterY:(<span class="hljs-built_in">CGFloat</span>)y{
    <span class="hljs-keyword">self</span><span class="hljs-variable">.center</span> = <span class="hljs-built_in">CGPointMake</span>(<span class="hljs-keyword">self</span><span class="hljs-variable">.center</span><span class="hljs-variable">.x</span>, y);
}

<span class="hljs-comment">/** 获取最大x */</span>
- (<span class="hljs-built_in">CGFloat</span>)maxX{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span><span class="hljs-variable">.x</span> + <span class="hljs-keyword">self</span><span class="hljs-variable">.width</span>;
}
<span class="hljs-comment">/** 获取最小x */</span>
- (<span class="hljs-built_in">CGFloat</span>)minX{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span><span class="hljs-variable">.x</span>;
}

<span class="hljs-comment">/** 获取最大y */</span>
- (<span class="hljs-built_in">CGFloat</span>)maxY{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span><span class="hljs-variable">.y</span> + <span class="hljs-keyword">self</span><span class="hljs-variable">.height</span>;
}
<span class="hljs-comment">/** 获取最小y */</span>
- (<span class="hljs-built_in">CGFloat</span>)minY{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span><span class="hljs-variable">.y</span>;
}

<span class="hljs-keyword">@end</span>

后记

Category在项目中的运用不仅仅这些,以上只是抛砖引玉。那么现在问题来了:想必看官们也有自己的一些使用心得吧,真心求分享(不分享的木JJ,自己看着办😡)。另,有任何问题欢迎提出😃

文/无夜之星辰(简书作者)
原文链接:http://www.jianshu.com/p/8f2b173263f9
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

iOS开发——Category在项目中的实际运用