一、Categories
类别是一种为现有的类添加新方法的方式,利用Objective-C的动态运行时分配机制,可以为现有的类添加新方法,它可以为任何类添加新的方法,包括那些没有源代码的类。在创建类别时,会生成.h和.m两个文件
一、声明类别
声明类别与声明类的形式很相似
@interface NSString(NumberConvenience)
-(NSNumber *)lengthAsNumber;
@end//NumberConvenience
二、实现类别
@implementation NSString(NumberConvenience)
-(NSNumber *)lengthAsNumber
{
unsigned int length = [self length];
return ([NSNumber numberWithUnsignedInt : length]);
} //lengthAsNumber
@end //NumberConvenience
三、类别的局限性
(1)**Categories只能添加方法,不能添加实例变量(**categories为运行期决议,此时对象内存布局已定)
(2)名称冲突,即当类别中的方法与原始类方法名称冲突时,类别具有更高的优先级。类别方法将完全取代初始方法从而无法再使用初始方法。
四、类别的作用
(1)将类的实现分散到多个不同文件或多个不同框架中。
(2)创建对私有方法的前向引用。
(3)将应该视为“私有”的方法归入peivate分类中,隐藏实现细节。
下面我们分别实践一下类别的作用
1、将类的实现分散到多个不同文件或多个不同框架中。
在一个类的实现中,我们可以将类的接口放入.h文件中,将类的实现放入.m文件中,但不可以将@implementation分散到多个不同的.m文件中,但利用类别,可以将一个类在@implementation方法按照方法的功能拆分到不同的类别中,使编程人员更加容易的阅读头文件
举例代码:
头文件CatagoryThing.h包含类的声明和一些类别,导入Foundation框架,然后带有3个整型变量的声明
# import
@interface CategoryThing : NSObject {
int thing1;
int thing2;
int thing3;
}
@end
//类声明之后是3个类别,每个类别具有一个实例变量的访问器,将这些实现分散到不同的文件中
@interface CategoryThing(Thing1)
- (void) setThing1: (int) thing1;
- (int) thing1;
@end
@interface CategoryThing (Thing2)
- (void) setThing2: (int) thing2;
- (int) thing2;
@end
@interface CategoryThing (Thing3)
- (void) setThing3: (int) thing3;
- (int) thing3;
@end
2、使用类别创建前向引用
如果其他类中的方法未实现,在你访问其他类的私有方法时编译器报错,这时使用类别,在类别中声明这些方法(不必提供方法实现),编译器就不会再产生警告
向类别中添加属性
(3)必须在类别中添加实例变量时怎么办?
方法一:
iOS中协议中和分类中是可以用@property形式声明属性的,只不过在协议、分类中声明的属性,只有对应的setter/getter方法,并没有生成对应的成员变量。因为协议中只可以声明方法,分类中只能声明方法和对应的实现。
Protocol
@protocol MyProtocol
@property (nonatomic, strong)NSString *protocolName;
@end
@interface ViewController :UIViewController
@end
如果一个类遵守这个协议的话,在该类中即可调用出self.protocolName,但是如果直接调用self.protocolName = @“就是个这个鬼”;
self.protocolName = @“这是个什么鬼啊”;
NSLog(@"%@",self.protocolName);
项目会直接出现崩溃
Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: '-[ViewController setProtocolName:]: unrecognized selector sent to instance 0x7f9fb350aa20’
实现方法1:
这是由于在协议当中的属性只有对应的setter/getter,但是没有成员变量,如果直接直接将其get方法写出如下:
- (NSString*)protocolName
{
return @“这个是个什么鬼”;
}
这样调用的话,就能够直接调用
NSLog(@"%@",self.protocolName);
输出结果为:
2018-04-10 15:30:36.373180+0800 这个是个什么鬼
实现方法2:
使用**@synthesize**告诉编译器需要生成对应的getter/setter方法以及实例变量
@interface ViewController ()
@end
@implementation ViewController
@synthesize protocolName =_protocolName;
使用@synthesize protocolName =_protocolName,编译器为 _protocolName 成员变量合成访问器方法,访问器方法操作的就是_protocolName这个变量。
self.protocolName = @“啊啊啊”;
NSLog(@"%@",self.protocolName);
2018-04-10 15:30:36.373180+0800****啊啊啊
实现方法3:
利用runtime黑魔法,通过runtime.h中objc_getAssociatedObject / objc_setAssociatedObject来访问和生成关联对象。这两个方法可以让一个对象和另一个对象关联,就是说一个对象可以保持对另一个对象的引用,并获取那个对象。
//NSObject+IndieBandName.h
@interface NSObject (IndieBandName)
@property (nonatomic, strong) NSString *indieBandName;
@end
//上面是头文件声明,下面的实现的.m文件:
// NSObject+IndieBandName.m
#import "NSObject+Extension.h"
#import <objc/runtime.h>
static const void *IndieBandNameKey = &IndieBandNameKey;
@implementation NSObject (IndieBandName)
@dynamic indieBandName;
- (NSString *)indieBandName
{
return objc_getAssociatedObject(self, IndieBandNameKey);
}
- (void)setIndieBandName:(NSString *)indieBandName
{
objc_setAssociatedObject(self, IndieBandNameKey, indieBandName, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
其他可以阅读下面两篇文章
美团技术团的一篇博文
转自:http://www.2cto.com/kf/201502/376993.html