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ò)展。

雖然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中存在我們在上面代碼中定義的方法和屬性以及屬性的setter和getter方法就可以說明類擴(kuò)展的確定時(shí)機(jī)是類編譯的時(shí)候。先導(dǎo)知識傳送門:
iOS Objective-C 類原理
iOS Objective-C 方法的本質(zhì)

通過上面這幅圖,我們已經(jīng)在ro的baseProperties中找到了我們類中已經(jīng)類擴(kuò)展中的所有屬性,以及在baseMethodList找到了我們類中和類擴(kuò)展中的方法已經(jīng)屬性的getter和setter方法,至此我們已經(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)對象 |