52个有效方法(24) - 将类的实现代码分散到便于管理的数个分
2018-09-05 本文已影响17人
SkyMing一C
分类(Category)
分类(Category)是OC中的特有语法,它是表示一个指向分类的结构体的指针。原则上它只能增加方法,不能增加成员(实例)变量。其定义如下:
typedef struct objc_category *Category;
struct objc_category {
char *category_name OBJC2_UNAVAILABLE; // 分类名
char *class_name OBJC2_UNAVAILABLE; // 分类所属的类名
struct objc_method_list *instance_methods OBJC2_UNAVAILABLE; // 实例方法列表
struct objc_method_list *class_methods OBJC2_UNAVAILABLE; // 类方法列表
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 分类所实现的协议列表
}
-
分类是用于给原有类添加方法的,因为分类的结构体指针中,没有属性列表,只有方法列表。所以< 原则上讲它只能添加方法, 不能添加属性(成员变量),实际上可以通过objc/runtime添加属性 > 。
-
分类中的可以写@property, 但不会生成setter/getter方法, 也不会生成实现以及私有的成员变量(编译时会报警告)。
-
可以在分类中访问原有类中.h中的属性。
-
如果分类中有和原有类同名的方法, 会优先调用分类中的方法, 就是说会忽略原有类的方法。所以同名方法调用的优先级为 分类 --> 本类 --> 父类。因此在开发中尽量不要覆盖原有类。
-
如果多个分类中都有和原有类中同名的方法, 那么调用该方法的时候执行谁由编译器决定;编译器会执行最后一个参与编译的分类中的方法。
分类格式
@interface 待扩展的类(分类的名称)
@end
@implementation 待扩展的名称(分类的名称)
@end
//.h
#import <Foundation/Foundation.h>
@interface EOCPerson : NSObject<NSCopying>
@property (nonatomic,copy) NSString *name;
@property (nonatomic,readonly) NSArray *friends;
@property (nonatomic,assign) int age;
@end
@interface EOCPerson (FriendShip)
- (void)addFriend:(EOCPerson *)person;
- (void)removeFriend:(EOCPerson *)person;
@end
@interface EOCPerson (Play)
- (void)playPingPong;
- (void)playFootball;
- (void)sing;
- (void)run;
@end
//.m
#import "EOCPerson.h"
@interface EOCPerson ()
@property (nonatomic,readwrite,strong) NSMutableArray *friends;
@end
@implementation EOCPerson
- (instancetype)initWithName:(NSString *)name age:(int)age
{
self = [super init];
if (self) {
self.name = name;
self.age = age;
_friends = [NSMutableArray array];
}
return self;
}
- (NSArray *)friends{
return [_friends copy];
}
- (id)copyWithZone:(NSZone *)zone{
EOCPerson *p = [[[self class] allocWithZone:zone] initWithName:_name age:_age];
p->_friends = [_friends mutableCopy];
return p;
}
@end
@implementation EOCPerson (FriendShip)
- (void)addFriend:(EOCPerson *)person{
if(person){
[_friends addObject:person];
}
}
- (void)removeFriend:(EOCPerson *)person{
if(person){
[_friends removeObject:person];
}
}
@end
@implementation EOCPerson (Play)
- (void)playPingPong {
}
- (void)playFootball {
}
- (void)sing{
}
- (void)run{
}
- (void)eat{
}
@end
分类优点
-
易于管理,方便检视
-
分类名称会出现在符号信息中
私有的方法
-
将视为私有的方法归入名叫private的分类中,以隐藏其细节
-
在编写分享给其他开发者使用的程序库时,可以考虑创建Private的分类。经常会遇到这样的一些方法:他们不是公共API的一部分,然而却非常适合在程序库之内使用。此时应该创建Private分类,如果程序库中某个地方要用到这些方法,就引入这个分类的头文件。而分类的头文件并不随程序库一并公开,于是该库的使用者并不知道这些方法。
-
如果调用者通过其他方式调用,在调试器中看到Private一词,便知道不能直接调用。
给分类添加属性
@interface NSObject (EOC_CX)
/**
* 为每一个对象添加一个name属性
*/
@property (nonatomic,copy) NSString *name;
/**
* 为每个对象添加一个View属性
*/
@property (nonatomic,strong) UIView *booksView;
/**
* 为每个对象添加一个是否被选中属性
*/
@property(nonatomic, assign) BOOL isSelected;
@end
- 第一种写法
#import "NSObject + EOC_CX .h"
#import <objc/runtime.h>
// 使用对象关联需引入#import <objc/runtime.h>头文件
@implementation NSObject (EOC_CX)
// 用一个字节来存储key值,设置为静态私有变量,避免外界修改
static void *nameKey;
- (void)setName:(NSString *)name
{
// 将某个值与某个对象关联起来,将某个值存储到某个对象中
objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name
{
return objc_getAssociatedObject(self, &nameKey);
}
static void *booksViewKey;
- (void)setBooksView:(UIView *) booksView
{
objc_setAssociatedObject(self, &booksViewKey, booksView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIView *) booksView
{
return objc_getAssociatedObject(self, &booksViewKey);
}
//将bool类型转变成NSNumber类型来进行添加属性 这样储存策略为OBJC_ASSOCIATION_COPY_NONATOMIC
static void *isSelectedKey;
- (void)setIsSelected:(BOOL)isSelected {
objc_setAssociatedObject(self, &isSelectedKey, @(isSelected), OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (BOOL)isSelected {
return [((NSNumber *) objc_getAssociatedObject(self, &isSelectedKey)) boolValue];
}
@end
- 第二种写法(该用法取自于: forkingdog / UITableView-FDTemplateLayoutCell 中的用法)
#import "NSObject + EOC_CX .h"
#import <objc/runtime.h>
// 使用对象关联需引入#import <objc/runtime.h>头文件
@implementation NSObject (EOC_CX)
- (void)setName:(NSString *)name
{
objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name
{
return objc_getAssociatedObject(self, _cmd);
//_cmd 代替了 &nameKey 或者 @selector(name).
}
- (void)setBooksView:(UIView *)booksView
{
objc_setAssociatedObject(self, @selector(booksView), booksView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIView *) booksView
{
return objc_getAssociatedObject(self, _cmd);
//_cmd 代替了 &booksKey 或者 @selector(booksView).
}
//将bool类型转变成NSNumber类型来进行添加属性 这样储存策略为OBJC_ASSOCIATION_COPY_NONATOMIC
- (void)setIsSelected:(BOOL)isSelected {
objc_setAssociatedObject(self, @selector(isSelected), @(isSelected), OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (BOOL)isSelected {
return [objc_getAssociatedObject(self, _cmd) boolValue];
//_cmd 代替了 &isSelectedKey 或者 @selector(isSelected).
}
@end
要点
-
使用分类机制把类的实现代码划分成易于管理的小块。
-
将应该视为“私有”的方法归入名叫private的分类中,以隐藏实现细节。