iOS Objective-C 類(lèi)擴(kuò)展
1. 類(lèi)擴(kuò)展簡(jiǎn)介
類(lèi)擴(kuò)展是我們?cè)陂_(kāi)發(fā)中經(jīng)常忽略的一個(gè)知識(shí)點(diǎn)乏梁。就我個(gè)人來(lái)說(shuō),一直認(rèn)為類(lèi)擴(kuò)展就是類(lèi)中的一部分律姨,我們主要在其中聲明私有屬性米苹,其實(shí)不是,因?yàn)轭?lèi)擴(kuò)展是單獨(dú)存在的忍啸,我們新建一個(gè)類(lèi)的時(shí)候并不會(huì)主動(dòng)創(chuàng)建類(lèi)擴(kuò)展仰坦。但也是,因?yàn)轭?lèi)擴(kuò)展在類(lèi)編譯的時(shí)候一起編譯计雌。
類(lèi)擴(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.
譯:類(lèi)擴(kuò)展與分類(lèi)有一些相似之處悄晃,但它只能添加到在編譯時(shí)擁有源代碼的類(lèi)中(類(lèi)與類(lèi)擴(kuò)展同時(shí)編譯)。類(lèi)擴(kuò)展聲明的方法是在原始類(lèi)的@implementation塊中實(shí)現(xiàn)的白粉,所以你不能在框架類(lèi)上聲明類(lèi)擴(kuò)展传泊,比如Cocoa或Cocoa Touch類(lèi),比如NSString鸭巴。
類(lèi)擴(kuò)展的聲明語(yǔ)法:
@interface ClassName ()
@end
新建一個(gè)類(lèi)擴(kuò)展:
我們?cè)?code>Xcode中創(chuàng)建Objective
類(lèi)型的文件的時(shí)候可以選擇空文件眷细、分類(lèi)、協(xié)議以及類(lèi)擴(kuò)展鹃祖。
雖然Xcode
給我們提供了新建類(lèi)擴(kuò)展的選項(xiàng)溪椎,但是一般我們不這樣用,我們一般都是在.m
文件中聲明一下當(dāng)前的類(lèi)擴(kuò)展恬口,其實(shí)協(xié)議的聲明我們大多數(shù)也是這樣做的校读。
2. 類(lèi)拓展的應(yīng)用
2.1 添加屬性
@interface XYZPerson ()
@property NSObject *extraProperty;
@end
2.2 添加實(shí)例變量
添加自定義實(shí)例變量。需要在類(lèi)擴(kuò)展接口的大括號(hào)中聲明祖能。
@interface XYZPerson () {
id _someCustomInstanceVariable;
}
@end
2.3 隱藏私有信息
在類(lèi)中聲明一個(gè)只讀的屬性uniqueIdentifier
如下歉秫,這樣我們暴露給外界的時(shí)候就是只讀的。
@interface XYZPerson : NSObject
...
@property (readonly) NSString *uniqueIdentifier;
- (void)assignUniqueIdentifier;
@end
但是我們想在類(lèi)內(nèi)部進(jìn)行更改就可以通過(guò)類(lèi)擴(kuò)展來(lái)實(shí)現(xiàn)养铸,如下
@interface XYZPerson ()
@property (readwrite) NSString *uniqueIdentifier;
@end
@implementation XYZPerson
...
@end
3. 驗(yàn)證類(lèi)擴(kuò)展的確定時(shí)機(jī)是編譯時(shí)
我們新建一個(gè)LGPerson
類(lèi)雁芙,并新建一個(gè)LGPerson+Extension
的類(lèi)擴(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í),類(lèi)中由編譯器確定的實(shí)例方法以及屬性存儲(chǔ)在類(lèi)的bits
->data
->ro
中兔甘,我們只要驗(yàn)證在ro
中存在我們?cè)谏厦娲a中定義的方法和屬性以及屬性的setter
和getter
方法就可以說(shuō)明類(lèi)擴(kuò)展的確定時(shí)機(jī)是類(lèi)編譯的時(shí)候谎碍。先導(dǎo)知識(shí)傳送門(mén):
iOS Objective-C 類(lèi)原理
iOS Objective-C 方法的本質(zhì)
通過(guò)上面這幅圖,我們已經(jīng)在ro
的baseProperties
中找到了我們類(lèi)中已經(jīng)類(lèi)擴(kuò)展中的所有屬性洞焙,以及在baseMethodList
找到了我們類(lèi)中和類(lèi)擴(kuò)展中的方法已經(jīng)屬性的getter
和setter
方法蟆淀,至此我們已經(jīng)驗(yàn)證了類(lèi)擴(kuò)展的確定時(shí)機(jī)是在編譯期。
但是有一點(diǎn)值得注意澡匪,如果我們沒(méi)有在類(lèi)的頭文件或者源文件中引入單獨(dú)的類(lèi)拓展頭文件熔任,那么這個(gè)單獨(dú)的類(lèi)拓展的頭文件里面的屬性和方法將不會(huì)被加載到類(lèi)上面來(lái)。會(huì)認(rèn)為你不用唁情,優(yōu)化掉了笋敞。
4. 類(lèi)擴(kuò)展和分類(lèi)的區(qū)別
按照官方的文檔的說(shuō)法,類(lèi)擴(kuò)展與分類(lèi)很相似荠瘪,也可以說(shuō)是匿名的分類(lèi)但是它們之間也有很多的區(qū)別夯巷。iOS Objective-C 分類(lèi)的加載
操作對(duì)象 | 是否有implementation | 加載時(shí)機(jī) | 操作對(duì)象 | 能否通過(guò)@property聲明屬性生成 getter 和 setter |
---|---|---|---|---|
類(lèi)擴(kuò)展 | 無(wú) | 編譯時(shí) | ro | 可以 |
分類(lèi) | 有 | 運(yùn)行時(shí)/編譯時(shí) | ro/rw | 不可以,需要借助關(guān)聯(lián)對(duì)象 |