iOS中类别与扩展
一、类别
类别就是类的扩展,在不改变类的结构的同时,可以像类中添加方法(不能添加成 员变量)
二、类别作用
- 可以将类的实现分散到多个不同文件或多个不同框架中,方便代码管理。也可以对框架提供类的扩展
- 使用匿名类别模拟私有方法
- 类别可以实现非正式协议
- 创建对私有方法的前向引用
其中对第四点进行解释:
创建一个Student类,有一个私有方法
<pre>
import <Foundation/Foundation.h>
@interface Student : NSObject
@end
</pre>
<pre>
import "Student.h"
@interface Student()
{
// 匿名类别中的成员变量,相当于全局变量来使用。
int a ;
}
-(void)privateMethod;
@implementation Student
// 实现的私有方法,不能在本类以外的任何地方调用。
-(void)privateMethod
{
NSLog(@"private mythod");
}
@end
</pre>
创建一个Student类的类别,.h文件声明了一个privateMethod,但是在.m中没有实现方法
<pre>
@interface Student (HavePrivateMethod)
-(void)privateMethod;
@end
</pre>
在 main.m中调用,记住import这个类别
<pre>
import <Foundation/Foundation.h>
import "Student.h"
import "Student+ HavePrivateMethod.h"
int main(int argc, const char * argv[])
{
@autoreleasepool {
Student *s1 = [[Student alloc] init];
[s privateMethod];
}
return 0;
}
</pre>
三、类别注意
- 通过继承关系,父类中的类别的方法,可以继承给子类。(想调用类别中的方法,就要引用头文件)。
- 类别中声明的方法可以不实现 (但是扩展不可以)
- 如果分类和原来类出现同名的方法,优先调用分类中的方法, 原来类中的方法会被忽略
四、给类别添加属性的方法
首先我们看下以下代码
<pre>
@interface UIView (nl_Frame)
@property (nonatomic, assign) CGFloat nl_width;
@end
</pre>
<pre>
@implementation UIView (nl_Frame)
-
(void)setNl_width:(CGFloat)nl_width {
CGRect frame = self.frame;
frame.size.width = nl_width;
self.frame = frame;
} -
(CGFloat)nl_width {
return CGRectGetWidth(self.frame);
}
@end
</pre>
在这里给 UIView增加了一个宽度属性:nl_width,而且为其实现了相应的 getter和 setter方法(nl_width、setNl_width:)。这两个方法实际上访问的 frame属性,如果你有注意过的话,你会发现frame也是定义在分类里边的:
<pre>
@interface UIView(UIViewGeometry)
@property(nonatomic) CGRect frame;
//...
@end
</pre>
可以看到,这种定义在分类里的属性,实际上是实现了相应的方法,并在方法里边通过访问其它属性来达到目的。这通常用来简化某些操作,比如定义咱们这个分类后,获取视图的宽度只要view.nl_width就可以了,再不用CGRectGetWidth(view.frame)来得到宽度,而且可读性也增强了很多。
当然还有另一种方法来实现类别属性
首先得先引入头文件:
<pre>
import <objc/runtime.h>
</pre>
可以在该头文件中找到三个允许你将任何键值在运行时关联到对象上的函数
<pre>
objc_setAssociatedObject // 设置关联对象
objc_getAssociatedObject // 获取关联对象
objc_removeAssociatedObjects // 移除关联对象
</pre>
知道这个之后我们来实践下添加属性
<pre>
@interface Person (Sport)
@property (nonatomic, assign) NSUInteger age;
@end
</pre>
<pre>
@implementation Person (Sport)
static void * age_key = & age_key;
-
(void) setAge:(NSUInteger)age {
objc_setAssociatedObject(self, age_key , @(age), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
} -
(NSUInteger)age {
return [objc_getAssociatedObject(self, age_key) unsignedLongValue];
}
@end
</pre>
关联对象在使用时,需要咱们提供一个指针,即key,用来识别被关联的对象。咱们这里的key就是一个空指针:nl_sqlite_rowid_key。当然,你也可以@selector(rowid)来作为 key(常用)。
于是,就可以这么来用了:
<pre>
Person *person = [Person new];
person.age = 1;
</pre>
注意咯:
<pre>
关联类型 等效的@property属性
OBJC_ASSOCIATION_ASSIGN assign
OBJC_ASSOCIATION_RETAIN_NONATOMIC nonatomic, retain
OBJC_ASSOCIATION_COPY_NONATOMIC nonatomic, copy
OBJC_ASSOCIATION_RETAIN retain
OBJC_ASSOCIATION_COPY copy
</pre>