类别(Category)
类别能够做到的事情主要是:即使在你不知道一个类的源码情况下,向这个类添加扩展的方法。
①通过在interface中声明一个额外的方法并且在implementation 中定义相同名字的方法即可。
②分类的名字(也就是括号括起来的LYG_CategoryName)表示的是:对于声明于其他地方的这个类(ClassName),在此处添加的方法是额外的,而不是表示这是一个新的类。
③你不可以通过分类为一个类添加额外的成员变量(但是可以使用@dynamic 来弥补这种不足--即运行时Runtime)。
④Category的方法不一定非要在@implementation中实现,也可以在其他位置实现,但是当调用Category的方法时,依据继承树没有找到该方法的实现,程序则会崩溃。
类别的使用三种场景:
- 扩展已有的类
类别特别适合已经存在大量子类,需要添加公用方法,但又无法修改它们父类(如系统类)的情形 - 引用父类未公开方法
- 实现简单协议
类别声明模板:
.h中
@interface ClassName (LYG_CategoryName)
- (void)lyg_addedMethod;
@end
.m中
@implementation ClassName (LYG_CategoryName)
- (void)lyg_addedMethod {
}
@end
- LYG--是我自定义的前缀,建议都加上一个自定义的前缀。
创建过程如下:
1
2
3
4
以下实例代码:
实例1:(需要记录上次的点击的cell,特此做的分类添加的属性)--特殊
#import <UIKit/UIKit.h>
@interface UITableView (SpokenHomeWorkTable)
@property (nonatomic,strong) NSIndexPath *recordIndexPath;
@end
#import "UITableView+SpokenHomeWorkTable.h"
#import <objc/runtime.h>
@implementation UITableView (SpokenHomeWorkTable)
static NSString *indexpathKey = @"recordIndexPath";
- (NSIndexPath *)recordIndexPath{
return objc_getAssociatedObject(self, &indexpathKey);
}
- (void)setRecordIndexPath:(NSIndexPath *)recordIndexPath{
objc_setAssociatedObject(self, &indexpathKey, recordIndexPath, OBJC_ASSOCIATION_RETAIN);
}
@end
实例2:扩展已有的类(自定义tabbar上的小红点)
#import <UIKit/UIKit.h>
@interface UITabBarController (badge)
/**
* 显示提示小红点标记
*
* @param index tabbar下标位置
*/
- (void)showBadgeOnItemIndex:(int)index;
/**
* 隐藏小红点标记
*
* @param index tabbar下标位置
*/
- (void)hideBadgeOnItemIndex:(int)index;
/**
* 移除小红点标记
*
* @param index tabbar下标位置
*/
- (void)removeBadgeOnItemIndex:(int)index;
@end
#import "UITabBarController+badge.h"
//#define TabbarItemNums 3.0 //tabbar的数量
@implementation UITabBarController (badge)
- (void)showBadgeOnItemIndex:(int)index{
//移除之前的小红点
[self removeBadgeOnItemIndex:index];
//新建小红点
UIView *badgeView = [[UIView alloc]init];
badgeView.tag = 888 + index;
badgeView.layer.cornerRadius = 5;
badgeView.backgroundColor = [UIColor redColor];
CGRect tabFrame = self.tabBar.frame;
//确定小红点的位置
NSInteger tabbarItemNums = 3;
if ([UserDefault boolForKey:KCreatEnglishTabbar]) {
tabbarItemNums = 4;
}
float percentX = (index +0.6) / tabbarItemNums;
CGFloat x = ceilf(percentX * tabFrame.size.width);
CGFloat y = ceilf(0.1 * tabFrame.size.height);
badgeView.frame = CGRectMake(x, y, 10, 10);
[self.tabBar addSubview:badgeView];
}
//移除小红点
- (void)hideBadgeOnItemIndex:(int)index{
[self removeBadgeOnItemIndex:index];
}
//按照tag值进行移除
- (void)removeBadgeOnItemIndex:(int)index{
for (UIView *subView in self.tabBar.subviews) {
if (subView.tag == 888+index) {
[subView removeFromSuperview];
}
}
}
/*
//显示
[self.tabBarController.tabBar showBadgeOnItemIndex:2];
//隐藏
[self.tabBarController.tabBar hideBadgeOnItemIndex:2]
//其它
// self.tabBarItem.badgeValue =@"10";
// UITabBarItem * item=[self.tabBarController.tabBar.items objectAtIndex:2];
// item.badgeValue=[NSString stringWithFormat:@"%d",10];
*/
@end
实例3: 引用父类未公开方法
(比如父类 XSDLabel)
XSDLabel.h
#import <UIKit/UIKit.h>
@interface XSDLabel : UILabel
@end
XSDLabel.m
#import "XSDLabel.h"
@implementation XSDLabel
- (void)giveTextRandomColor {
self.textColor = [UIColor orangeColor];
}
@end
XSDLabel1继承自XSDLabel:
#import <UIKit/UIKit.h>
#import "XSDLabel.h"
@interface XSDLabel1 : XSDLabel
@end
现在需要在设置text时,同时设置文字颜色,调用父类的giveTextRandomColor:
#import "XSDLabel1.h"
@implementation XSDLabel1
- (void)setText:(NSString *)text {
[super setText:text];
[self giveTextRandomColor];
}
@end
直接编译会报错:编译器提示找不到父类的方法
在子类中声明父类类别后,即可通过编译:
#import "XSDLabel1.h"
@interface XSDLabel (private)
- (void)giveTextRandomColor;
@end
@implementation XSDLabel1
- (void)setText:(NSString *)text {
[super setText:text];
[self giveTextRandomColor];
}
@end
类别名private是任意的,但不可以缺省。
- 请不要乱来:苹果官方会拒绝使用系统私有API的应用上架,因此即使学会了如何调用私有方法,在遇到调用其它类的私有方法时,要谨慎处理,尽量用其它方法替代。
实例4: 实现简单协议
假设我们需要在文字颜色改变时,发出一个消息,现在修改XSDLabel如下:
#import <UIKit/UIKit.h>
@interface XSDLabel : UILabel
@property(nonatomic) id delegate;
@end
@interface NSObject (XSDLabelDelegateMethods)
- (void)textColorChanged:(UIColor *)colorNow;
@end
增加delegate,声明为id,表示接受任何类。
声明NSObject的类别,声明它实现的方法。
#import "XSDLabel.h"
@implementation XSDLabel
- (void)giveTextRandomColor {
self.textColor = [UIColor orangeColor];
[self.delegate textColorChanged:self.textColor]; // 调用代理的方法
}
@end
调用的地方:
#import "XSDLabel1.h"
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
XSDLabel1 *label = [[XSDLabel1 alloc] initWithFrame:CGRectMake(10.0, 40.0, 100.0, 30.0)];
[self.view addSubview:label];
label.delegate = self;
label.text = @"溪石iOS";
}
- (void)textColorChanged:(UIColor *)colorNow {
NSLog(@"text color changed to %@", colorNow);
}
这里利用了任何类都是NSObject子类的特点,通过添加NSObject的类别,实现了一个“简单”的代理协议。
对比“正式的协议”,这种协议不需要实现类显示声明(如<NSCopying>),不过这里还有个缺点,当ViewController未实现textColorChanged方法时,会引发崩溃,因此在调用前,需要检查代理方法是否被实现:
#import "XSDLabel.h"
@implementation XSDLabel
- (void)giveTextRandomColor {
self.textColor = [UIColor orangeColor];
if ([self.delegate respondsToSelector:@selector(textColorChanged:)]) {
[self.delegate textColorChanged:self.textColor];
}
}
@end
扩展(Extension)
类扩展就像匿名(也就是没有那个括号里面的名字LYG_CategoryName)的分类一样,除了一样不同的是,类扩展声明必须在@implementation在实现。
// .h
@interface BaseClass : NSObject
@property (readonly) NSString *privateString; // 该.h文件对外公开
@end
// .m
@interface BaseClass() // 该.m文件对外是不公开的, 当然这里也可以放在专门的一个.h文件中,但同样不把这个文件进行公开。
@property (readwrite) NSString *privateString;
@end
@implementation BaseClass
@synthesize privateString;
//...
@end
声明的方法必须要实现,不然编译器会提出警告。
从上面看出,分类和类扩展的相似之处是:都可以为类添加一个额外的方法;
不同之处在于:要添加额外方法,分类必须在第一个@interface中声明方法,并且在@implementation中提供实现,不然运行时出错。而类扩展,你添加的方法是一个required API,如果不去实现,编译器会警告,而且这个方法的声明可以不在第一个@interface中去声明。
区别:
分类:是不可以声明实例变量,通常是公开的,文件名通常为:”主类类名+分类类名.h”
扩展:是可以声明实例变量,是私有的,文件名通常为:”主类类名_扩展标识.h”,注意扩展没有名的。
区别分类与扩展
1.都可以在主类中声明使用
2.通常来讲由于分类不能创建实例变化,本质上与主类有区别,所以不建议写在主类中。
3.扩展与主类紧密联系在一起,可以创建实例变量,所以通常来讲会把扩展和主类创建在一起。
网友评论