美文网首页闻道丶iOS(大杂烩)iOS Developer
iOS 类别(Category)与扩展(Extension)

iOS 类别(Category)与扩展(Extension)

作者: SunnyLeong | 来源:发表于2017-03-13 16:08 被阅读136次

类别(Category)

类别能够做到的事情主要是:即使在你不知道一个类的源码情况下,向这个类添加扩展的方法。

①通过在interface中声明一个额外的方法并且在implementation 中定义相同名字的方法即可。

②分类的名字(也就是括号括起来的LYG_CategoryName)表示的是:对于声明于其他地方的这个类(ClassName),在此处添加的方法是额外的,而不是表示这是一个新的类。

③你不可以通过分类为一个类添加额外的成员变量(但是可以使用@dynamic 来弥补这种不足--即运行时Runtime)。

④Category的方法不一定非要在@implementation中实现,也可以在其他位置实现,但是当调用Category的方法时,依据继承树没有找到该方法的实现,程序则会崩溃。

类别的使用三种场景:

  1. 扩展已有的类
    类别特别适合已经存在大量子类,需要添加公用方法,但又无法修改它们父类(如系统类)的情形
  2. 引用父类未公开方法
  3. 实现简单协议

类别声明模板:

.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.扩展与主类紧密联系在一起,可以创建实例变量,所以通常来讲会把扩展和主类创建在一起。


相关文章

网友评论

    本文标题:iOS 类别(Category)与扩展(Extension)

    本文链接:https://www.haomeiwen.com/subject/tiifnttx.html