iOS Objective-C 類擴(kuò)展

iOS Objective-C 類擴(kuò)展

1. 類擴(kuò)展簡介

類擴(kuò)展是我們在開發(fā)中經(jīng)常忽略的一個(gè)知識點(diǎn)。就我個(gè)人來說,一直認(rèn)為類擴(kuò)展就是類中的一部分,我們主要在其中聲明私有屬性,其實(shí)不是,因?yàn)轭悢U(kuò)展是單獨(dú)存在的,我們新建一個(gè)類的時(shí)候并不會主動創(chuàng)建類擴(kuò)展。但也是,因?yàn)轭悢U(kuò)展在類編譯的時(shí)候一起編譯。

類擴(kuò)展的定義:

A class extension bears some similarity to a category, but it can only be added to a class for which you have the source code at compile time (the class is compiled at the same time as the class extension). The methods declared by a class extension are implemented in the @implementation block for the original class so you can’t, for example, declare a class extension on a framework class, such as a Cocoa or Cocoa Touch class like NSString.

譯:類擴(kuò)展與分類有一些相似之處,但它只能添加到在編譯時(shí)擁有源代碼的類中(類與類擴(kuò)展同時(shí)編譯)。類擴(kuò)展聲明的方法是在原始類的@implementation塊中實(shí)現(xiàn)的,所以你不能在框架類上聲明類擴(kuò)展,比如Cocoa或Cocoa Touch類,比如NSString。

類擴(kuò)展的聲明語法:

@interface ClassName ()
 
@end

新建一個(gè)類擴(kuò)展:

我們在Xcode中創(chuàng)建Objective類型的文件的時(shí)候可以選擇空文件、分類、協(xié)議以及類擴(kuò)展。

新建類擴(kuò)展.jpg

雖然Xcode給我們提供了新建類擴(kuò)展的選項(xiàng),但是一般我們不這樣用,我們一般都是在.m文件中聲明一下當(dāng)前的類擴(kuò)展,其實(shí)協(xié)議的聲明我們大多數(shù)也是這樣做的。

2. 類拓展的應(yīng)用

2.1 添加屬性

@interface XYZPerson ()
@property NSObject *extraProperty;
@end

2.2 添加實(shí)例變量

添加自定義實(shí)例變量。需要在類擴(kuò)展接口的大括號中聲明。

@interface XYZPerson () {
    id _someCustomInstanceVariable;
}
@end

2.3 隱藏私有信息

在類中聲明一個(gè)只讀的屬性uniqueIdentifier如下,這樣我們暴露給外界的時(shí)候就是只讀的。

@interface XYZPerson : NSObject
...
@property (readonly) NSString *uniqueIdentifier;
- (void)assignUniqueIdentifier;
@end

但是我們想在類內(nèi)部進(jìn)行更改就可以通過類擴(kuò)展來實(shí)現(xiàn),如下

@interface XYZPerson ()
@property (readwrite) NSString *uniqueIdentifier;
@end
 
@implementation XYZPerson
...
@end

3. 驗(yàn)證類擴(kuò)展的確定時(shí)機(jī)是編譯時(shí)

我們新建一個(gè)LGPerson類,并新建一個(gè)LGPerson+Extension的類擴(kuò)展。代碼如下:

LGPerson 代碼:


#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface LGPerson : NSObject
@property (nonatomic, copy) NSString *name;
@end

NS_ASSUME_NONNULL_END


#import "LGPerson.h"
#import "LGPerson+Extension.h"

@interface LGPerson ()
@property (nonatomic, copy) NSString *mName;

- (void)extM_method;
@end

@implementation LGPerson

+ (void)load{
    NSLog(@"%s",__func__);
}

- (void)extM_method{
    NSLog(@"%s",__func__);
}

- (void)extH_method{
    NSLog(@"%s",__func__);
}

@end

LGPerson+Extension 代碼:

#import "LGPerson.h"


NS_ASSUME_NONNULL_BEGIN

@interface LGPerson ()
@property (nonatomic, copy) NSString *ext_name;
@property (nonatomic, copy) NSString *ext_subject;

- (void)extH_method;
@end

NS_ASSUME_NONNULL_END

main.m 代碼:

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "LGPerson.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
        
        LGPerson *p = [LGPerson alloc];
        NSLog(@"%@ - %p", p, p);
        
    }
    return 0;
}

先導(dǎo)知識,類中由編譯器確定的實(shí)例方法以及屬性存儲在類的bits->data->ro中,我們只要驗(yàn)證在ro中存在我們在上面代碼中定義的方法和屬性以及屬性的settergetter方法就可以說明類擴(kuò)展的確定時(shí)機(jī)是類編譯的時(shí)候。先導(dǎo)知識傳送門:
iOS Objective-C 類原理
iOS Objective-C 方法的本質(zhì)

lldb.jpg

通過上面這幅圖,我們已經(jīng)在robaseProperties中找到了我們類中已經(jīng)類擴(kuò)展中的所有屬性,以及在baseMethodList找到了我們類中和類擴(kuò)展中的方法已經(jīng)屬性的gettersetter方法,至此我們已經(jīng)驗(yàn)證了類擴(kuò)展的確定時(shí)機(jī)是在編譯期。

但是有一點(diǎn)值得注意,如果我們沒有在類的頭文件或者源文件中引入單獨(dú)的類拓展頭文件,那么這個(gè)單獨(dú)的類拓展的頭文件里面的屬性和方法將不會被加載到類上面來。會認(rèn)為你不用,優(yōu)化掉了。

4. 類擴(kuò)展和分類的區(qū)別

按照官方的文檔的說法,類擴(kuò)展與分類很相似,也可以說是匿名的分類但是它們之間也有很多的區(qū)別。iOS Objective-C 分類的加載

操作對象 是否有implementation 加載時(shí)機(jī) 操作對象 能否通過@property聲明屬性生成 getter 和 setter
類擴(kuò)展 編譯時(shí) ro 可以
分類 運(yùn)行時(shí)/編譯時(shí) ro/rw 不可以,需要借助關(guān)聯(lián)對象

5. 參考資料

Customizing Existing Classes

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

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