iOS 類別(Category)與擴展(Extension)

類別(Category)

類別能夠做到的事情主要是:即使在你不知道一個類的源碼情況下,向這個類添加擴展的方法。

①通過在interface中聲明一個額外的方法并且在implementation 中定義相同名字的方法即可。

②分類的名字(也就是括號括起來的LYG_CategoryName)表示的是:對于聲明于其他地方的這個類(ClassName),在此處添加的方法是額外的,而不是表示這是一個新的類。

③你不可以通過分類為一個類添加額外的成員變量(但是可以使用@dynamic 來彌補這種不足--即運行時Runtime)。

④Category的方法不一定非要在@implementation中實現(xiàn),也可以在其他位置實現(xiàn),但是當調(diào)用Category的方法時,依據(jù)繼承樹沒有找到該方法的實現(xiàn),程序則會崩潰。

類別的使用三種場景:

  1. 擴展已有的類
    類別特別適合已經(jīng)存在大量子類,需要添加公用方法,但又無法修改它們父類(如系統(tǒng)類)的情形
  2. 引用父類未公開方法
  3. 實現(xiàn)簡單協(xié)議

類別聲明模板:

.h中

@interface ClassName (LYG_CategoryName)
- (void)lyg_addedMethod;
@end

.m中

@implementation ClassName (LYG_CategoryName)
- (void)lyg_addedMethod {
}
@end
  • LYG--是我自定義的前綴,建議都加上一個自定義的前綴。

創(chuàng)建過程如下:

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的數(shù)量

@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

現(xiàn)在需要在設置text時,同時設置文字顏色,調(diào)用父類的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是任意的,但不可以缺省。

  • 請不要亂來:蘋果官方會拒絕使用系統(tǒng)私有API的應用上架,因此即使學會了如何調(diào)用私有方法,在遇到調(diào)用其它類的私有方法時,要謹慎處理,盡量用其它方法替代。

實例4: 實現(xiàn)簡單協(xié)議

假設我們需要在文字顏色改變時,發(fā)出一個消息,現(xiàn)在修改XSDLabel如下:

#import <UIKit/UIKit.h>

@interface XSDLabel : UILabel
@property(nonatomic) id delegate;
@end

@interface NSObject (XSDLabelDelegateMethods)
- (void)textColorChanged:(UIColor *)colorNow;
@end

增加delegate,聲明為id,表示接受任何類。
聲明NSObject的類別,聲明它實現(xiàn)的方法。

#import "XSDLabel.h"

@implementation XSDLabel
- (void)giveTextRandomColor {
    self.textColor = [UIColor orangeColor];
    [self.delegate textColorChanged:self.textColor]; // 調(diào)用代理的方法
}
@end

調(diào)用的地方:

#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的類別,實現(xiàn)了一個“簡單”的代理協(xié)議。
對比“正式的協(xié)議”,這種協(xié)議不需要實現(xiàn)類顯示聲明(如<NSCopying>),不過這里還有個缺點,當ViewController未實現(xiàn)textColorChanged方法時,會引發(fā)崩潰,因此在調(diào)用前,需要檢查代理方法是否被實現(xiàn):

#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在實現(xiàn)。

// .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

聲明的方法必須要實現(xiàn),不然編譯器會提出警告。
從上面看出,分類和類擴展的相似之處是:都可以為類添加一個額外的方法;

不同之處在于:要添加額外方法,分類必須在第一個@interface中聲明方法,并且在@implementation中提供實現(xiàn),不然運行時出錯。而類擴展,你添加的方法是一個required API,如果不去實現(xiàn),編譯器會警告,而且這個方法的聲明可以不在第一個@interface中去聲明。
區(qū)別:

分類:是不可以聲明實例變量,通常是公開的,文件名通常為:”主類類名+分類類名.h”

擴展:是可以聲明實例變量,是私有的,文件名通常為:”主類類名_擴展標識.h”,注意擴展沒有名的。

區(qū)別分類與擴展

1.都可以在主類中聲明使用

2.通常來講由于分類不能創(chuàng)建實例變化,本質(zhì)上與主類有區(qū)別,所以不建議寫在主類中。

3.擴展與主類緊密聯(lián)系在一起,可以創(chuàng)建實例變量,所以通常來講會把擴展和主類創(chuàng)建在一起。


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容