說到runtime乍构。所有iOS的開發(fā)者無不知曉甜无。運(yùn)行時、swizzle哥遮、黑魔法等等岂丘。
不過用的時候是copy代碼、還是真正理解了runtime以及OC中類眠饮、對象奥帘、方法的本質(zhì)結(jié)構(gòu)。
起碼就我而言仪召、很長一段時間(以年來計算)寨蹋。都是前者。
所以這篇文章不屬于教學(xué)貼扔茅。希望借此能在runtime已旧、以及OC的本質(zhì)方面更深一步。
這里有一篇很不錯的入門文章召娜、內(nèi)部對runtime進(jìn)行了蠻詳細(xì)的歸納總結(jié)运褪。并且還列舉了很多runtime的實(shí)用帖子鏈接。
iOS 模塊詳解—「Runtime面試萤晴、工作」看我就 ?? 了 _
目錄
- 類和對象
- 實(shí)例對象(id)
- 類對象(Class)
- 元類(Meta Class)
- 類與對象的總結(jié)
- runtime正題
- runtime方法的前綴
- 對象操作方法(object_)
- 全局操作方法(objc_)
- 類操作方法(class_)
- 動態(tài)創(chuàng)建類和對象
- 動態(tài)創(chuàng)建類
- 動態(tài)創(chuàng)建對象
- 方法操作(method_)
- 實(shí)例變量操作(ivar_)
- 通過偏移量
ivar_offset
吐句、獲取/修改任意一個成員變量。
- 通過偏移量
- 屬性操作(property_)
- 屬性的動態(tài)添加與獲取
- 協(xié)議操作(protocol_)
- 動態(tài)的創(chuàng)建一個協(xié)議
- 庫操作
- 選擇器操作(sel_)
- 語言特性
- 關(guān)聯(lián)策略
- 編碼類型
類和對象
為什么runtime的博客店读、要聊究類和對象嗦枢?
或許寫出一段swizzle代碼更直觀
- (void)runtime_swizzle_test {
Method method1 = class_getInstanceMethod([self class], @selector(func1));
Method method2 = class_getInstanceMethod([self class], @selector(func2));
method_exchangeImplementations(method1, method2);
}
所有的runtime方法、只要與方法操作相關(guān)屯断。
無一例外都需要使用Method
或者其結(jié)構(gòu)體內(nèi)部的IMP文虏、SEL等指針。
而這個Method
恰恰存放在類
的結(jié)構(gòu)體中殖演。
除此之外:
-
class_getInstanceMethod
(獲取Method)氧秘、class_copyIvarList
(獲取屬性列表)。也都是建立在class_
也就是類操作之下趴久。 -
object_setIvar
(屬性賦值)丸相。則是建立在object_
、也就是對象操作之下彼棍。
所以灭忠、大概可以知道了解類和對象對理解runtime有多么大的幫助了膳算。
我們每天都在創(chuàng)建類、或者類的實(shí)例對象弛作。但類和實(shí)例對象到底是什么涕蜂?
[XXX new]
或者[[XXX alloc]init]
就是實(shí)例對象?
說得對、但是這淺顯了映琳。相信如果面試官如此提問机隙、你絕壁不敢這么回答的。
-
實(shí)例對象(id)
id obj = self;
這就是我們的OC對象萨西。
/// A pointer to an instance of a class.
/// 指向一個實(shí)例的指針
typedef struct objc_object *id;
/// Represents an instance of a class.
/// 表示類的實(shí)例有鹿。
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
- 實(shí)例對象在被創(chuàng)建的過程中會
拷貝
實(shí)例所屬的類的成員變量
、但并不拷貝
類定義的方法
原杂。 - isa
整個id對象的結(jié)構(gòu)體內(nèi)部印颤、只有一個isa指針您机、指向了其所屬的Class
穿肄。 - 調(diào)用
實(shí)例方法
時。
系統(tǒng)會根據(jù)實(shí)例的isa
指針去類的方法列表及父類的方法列表中尋找與消息對應(yīng)的selector指向的方法际看。
-
類對象(Class)
Class class = [self class];
這就是我們每天都在使用的Class
咸产、也就是類。點(diǎn)進(jìn)去
#import <objc/objc.h>
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
可以看到Class
本質(zhì)是一個objc_class
的結(jié)構(gòu)體仲闽。繼續(xù)
這里為了看著舒服脑溢、我把一些標(biāo)注的宏刪掉了。
struct objc_class {
Class isa ; // 指向所屬類的指針(_Nonnull)
Class super_class; // 父類(_Nullable)
const char * name; // 類名(_Nonnull)
long version; // 類的版本信息(默認(rèn)為0)
long info; // 類信息(供運(yùn)行期使用的一些位標(biāo)識)
long instance_size; // 該類的實(shí)例變量大小
struct objc_ivar_list * ivars; // 該類的成員變量鏈表(_Nullable)
struct objc_method_list ** methodLists ; // 方法定義的鏈表(_Nullable)
struct objc_cache * cache // 方法緩存(_Nonnull)
struct objc_protocol_list * protocols; // 協(xié)議鏈表(_Nullable)
} ;
- isa
Class
的結(jié)構(gòu)體中也有isa
指針赖欣、但和對象不同
的是屑彻。Class
的isa
指向的是Class
所屬的類、即元類亲茅。 - 調(diào)用
類方法時
系統(tǒng)會通過該isa指針從元類
中尋找方法對應(yīng)的函數(shù)指針横堡。 - super_class
父類指針出牧、如果該類已經(jīng)是最頂層的根類(如NSObject或NSProxy)、則 super_class為NULL搏恤。 - ivars
objc_ivar_list類型的指針,用來存儲這個類中所有成員變量的信息湃交。
//變量列表
struct objc_ivar_list {
int ivar_count ;
#ifdef __LP64__
int space ;
#endif
/* variable length structure */
struct objc_ivar ivar_list[1] ;
}
//單個變量信息
struct objc_ivar {
char * _Nullable ivar_name ;
char * _Nullable ivar_type ;
int ivar_offset ; //基地址偏移字節(jié)
#ifdef __LP64__
int space ;
#endif
}
- methodLists
//方法列表
struct objc_method_list {
struct objc_method_list * _Nullable obsolete ;
int method_count ;
#ifdef __LP64__
int space ;
#endif
/* variable length structure */
struct objc_method method_list[1]
}
//單個方法的信息
struct objc_method {
SEL _Nonnull method_name ;
char * _Nullable method_types ;
IMP _Nonnull method_imp ;
}
- objc_cache
struct objc_cache {
unsigned int mask /* total = mask + 1 */ OBJC2_UNAVAILABLE;
unsigned int occupied OBJC2_UNAVAILABLE;
Method buckets[1] OBJC2_UNAVAILABLE;
};
用于緩存最近使用的方法熟空。系統(tǒng)在調(diào)用方法時會先去cache中查找、在沒有查找到時才會去methodLists中遍歷獲取需要的方法搞莺。
-
元類(Meta Class)
上面我們看到了息罗。在objc_class
中也有一個isa
指針、這說明Class
類本身也是一個對象才沧。為了處理類和對象的關(guān)系迈喉、Runtime 庫創(chuàng)建了一種叫做Meta Class
(元類) 的東西俏扩、類對象所屬的類就叫做元類。Meta Class
表述了類對象本身所具備的元數(shù)據(jù)弊添。
-
元類
用來儲存類方法
录淡。 - 元類也是一個對象、所以才能調(diào)用他的方法油坝。
- 元類的元類嫉戚、為根元類。
- 根元類的元類澈圈、是其本身彬檀。
更多的關(guān)于元類的就暫且打住~畢竟對于runtime。了解這些元類和類的關(guān)系已經(jīng)夠用了瞬女。
-
類與對象的總結(jié)
還有一張?zhí)貏e經(jīng)典的圖
規(guī)則一: 實(shí)例對象的isa指向該類窍帝、類的isa指向元類(metaClass)。
規(guī)則二: 類的superClass指向其父類诽偷、如果該類為根類則值為nil坤学。
規(guī)則三: 元類的isa指向根元類、如果該元類是根元類則指向自身报慕。
規(guī)則四: 元類的superClass指向父元類深浮、若根元類則指向該根類。
runtime正題
runtime方法的前綴
runtime方法有很多眠冈、最方便歸納的方式就是通過前綴來確定其作用對象飞苇。
對象操作方法(object_
)
包含了對對象的所有操作。
拷貝蜗顽、釋放布卡、獲取所屬類(isa
)、所屬成員變量的操作(初始化的時候會copy成員變量列表
)
/**
* 返回給定對象的副本雇盖。
*/
id object_copy(id _Nullable obj, size_t size)
/**
* 釋放對象內(nèi)存
*/
id object_dispose(id _Nullable obj)
/**
* 返回對象的類--也就是上文說的isa指針?biāo)傅念? */
Class object_getClass(id _Nullable obj)
/**
* 為對象設(shè)置一個新的類
* 其實(shí)就等于 NSArray *arr = [NSMutableArray new];
* 這個arr依舊是_M類型忿等、但是已經(jīng)不能使用addobj方法了、起碼在編譯階段是刊懈。
*/
Class object_setClass(id _Nullable obj, Class _Nonnull cls)
/**
* 判斷一個對象是否為Class類型的對象
*/
BOOL object_isClass(id _Nullable obj)
/**
* 返回一個對象中指定實(shí)例變量(Ivar)的值
*/
id object_getIvar(id _Nullable obj, Ivar _Nonnull ivar)
/*
* 為對象設(shè)置一個實(shí)例變量的值
*/
void object_setIvar(id _Nullable obj, Ivar _Nonnull ivar, id _Nullable value)
/*
* 10.0以后多了一個方法
*/
void object_setIvarWithStrongDefault(id _Nullable obj, Ivar _Nonnull ivar,
id _Nullable value)
/*
* 非ARC下的兩個set方法
*/
void object_setInstanceVariable(id _Nullable obj, const char * _Nonnull name,
void * _Nullable value);
void object_setInstanceVariableWithStrongDefault(id _Nullable obj,
const char * _Nonnull name,
void * _Nullable value)
/*
* 非ARC下的get方法
*/
Ivar object_getInstanceVariable(id _Nullable obj, const char * _Nonnull name,
void * _Nullable * _Nullable outValue)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0)
OBJC_ARC_UNAVAILABLE;
其中有比較特殊的:
-
Class object_getClass(id _Nullable obj)
這個方法可以獲得對象結(jié)構(gòu)體內(nèi)部isa指針?biāo)刚饣 ⒁簿褪菍ο笏鶎俚念悺?具體是啥可以翻回去看)。
但需要注意的是虚汛、這個和我們平時用的[self class]
方法匾浪、并不完全等價。
+ (Class)class {
return self; // 返回自身指針
}
- (Class)class {
return object_getClass(self); // 調(diào)用'object_getClass'返回isa指針
}
也就是說卷哩、通過[Class object_getClass]
是可以獲取元類的蛋辈、但是通過[Class class]
則不行。
-
void object_setIvar(id _Nullable obj, Ivar _Nonnull ivar, id _Nullable value)
為對象設(shè)置一個實(shí)例變量的值
但依舊不能為不存在的變量賦值
當(dāng)獲取一個不存在實(shí)例變量時、Ivar將等于0x0000000000000000冷溶。自然沒辦法對其進(jìn)行賦值渐白。
全局操作方法(objc_
)
這里的方法并不基于對象、也不需要以對象作為參數(shù)逞频。
基本都是一些類相關(guān)的獲取纯衍。
/*
* 返回指定的Class
* 如果未注冊命名類的定義,則該函數(shù)調(diào)用類處理程序苗胀。
* 然后再次檢查是否注冊了類襟诸。
*/
Class objc_getClass(const char * _Nonnull name)
/*
* 返回指定的元類Class
*/
Class objc_getMetaClass(const char * _Nonnull name)
/*
* 返回指定的Class
* 不調(diào)用類處理程序回調(diào)。
*/
Class objc_lookUpClass(const char * _Nonnull name)
/*
* 返回指定的Class
* 如果沒有找到類基协,就會殺死進(jìn)程歌亲。
*/
Class objc_getRequiredClass(const char * _Nonnull name)
/*
* 獲取所有已經(jīng)注冊類的列表
*/
int objc_getClassList(Class _Nonnull * _Nullable buffer, int bufferCount)
/*
* 獲取所有已經(jīng)注冊類的實(shí)例列表
*/
Class *objc_copyClassList(unsigned int * _Nullable outCount)
其中有比較特殊的:
- 獲取所有已經(jīng)注冊的類--數(shù)量.
int objc_getClassList(Class _Nonnull * _Nullable buffer, int bufferCount)
int numClasses = 0, newNumClasses = objc_getClassList(NULL, 0);
Class *classes = NULL;
while (numClasses < newNumClasses) {
numClasses = newNumClasses;
classes = (Class *)realloc(classes, sizeof(Class) * numClasses);
newNumClasses = objc_getClassList(classes, numClasses);
for (int i = 0; i < numClasses; i++) {
const char *className = class_getName(classes[i]);
if (class_getSuperclass(classes[i]) == [UIScrollView class]) {
//打印所有UIScrollView的子類
NSLog(@"subclass of UIScrollView : %s", className);
}
}
}
free(classes);
- 獲取所有已經(jīng)注冊的類--實(shí)例列表.
Class *objc_copyClassList(unsigned int * _Nullable outCount)
unsigned int outCount;
Class *classes = objc_copyClassList(&outCount);
for (int i = 0; i < outCount; i++) {
if (class_getSuperclass(classes[i]) == [UIScrollView class]) {
//打印所有UIScrollView的子類
NSLog(@"subclass of UIScrollView : %s", class_getName(classes[i]));
}
}
free(classes);
類對象操作方法(class_
)
這里的所有操作都針對類對象
/**
* 返回類的名稱
*/
char * class_getName(Class _Nullable cls)
/**
* 判斷該類對象是否為元類
*/
BOOL class_isMetaClass(Class _Nullable cls)
/**
* 獲取結(jié)構(gòu)體中的super_class、也就是父類對象指針
*/
Class class_getSuperclass(Class _Nullable cls)
/**
* 給一個類對象設(shè)置新的父類指針
* 返回舊的父類
* 這個方法已經(jīng)被棄用并且apple明確指出不應(yīng)該使用澜驮。
*/
Class class_setSuperclass(Class _Nonnull cls, Class _Nonnull newSuper)
/**
* 獲取類對象的版本號
* 沒發(fā)現(xiàn)啥用處陷揪、不過測試起來
* 所有的元類都為7
* 普通類NSDateFormatter最高(41)、并且大于5的只有它一個
*/
int class_getVersion(Class _Nullable cls)
/**
* 為一個類設(shè)置版本號
*/
void class_setVersion(Class _Nullable cls, int version)
/**
* 獲取一個類的實(shí)例大小
*/
size_t class_getInstanceSize(Class _Nullable cls)
/**
* 獲取類中指定名稱實(shí)例成員變量的信息(Ivar)
*/
Ivar class_getInstanceVariable(Class _Nullable cls, const char * _Nonnull name)
/**
* 獲取類"成員變量"的信息(Ivar)
* 但是好像OC中沒有這種東西
*/
Ivar class_getClassVariable(Class _Nullable cls, const char * _Nonnull name)
/**
* 獲取全部成員變量列表
* 但是不包括父類
*/
Ivar * class_copyIvarList(Class _Nullable cls, unsigned int * _Nullable outCount)
/**
* 查找某個實(shí)例方法
*/
Method class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name)
/**
* 查找某個類方法
*/
Method class_getClassMethod(Class _Nullable cls, SEL _Nonnull name)
/**
* 查找某個實(shí)例方法的實(shí)現(xiàn)(IMP)
*/
IMP class_getMethodImplementation(Class _Nullable cls, SEL _Nonnull name)
/**
* 查找某個實(shí)例方法的實(shí)現(xiàn)(IMP)
* 不過在“ arm64”也就是現(xiàn)在的機(jī)器上報報紅
*/
IMP class_getMethodImplementation_stret(Class _Nullable cls, SEL _Nonnull name)
/**
* 查找某個實(shí)例方法的實(shí)現(xiàn)(IMP)
* 不過在“ arm64”也就是現(xiàn)在的機(jī)器上報報紅
*/
BOOL class_respondsToSelector(Class _Nullable cls, SEL _Nonnull sel)
/**
* 返回該類所有方法(結(jié)構(gòu)體中的objc_method_list)
*/
Method * class_copyMethodList(Class _Nullable cls, unsigned int * _Nullable outCount)
/**
* 判斷類是否實(shí)現(xiàn)了某個協(xié)議
* 其實(shí)就是[self conformsToProtocol:@protocol(UITableViewDelegate)]的底層
*/
BOOL class_conformsToProtocol(Class _Nullable cls, Protocol * _Nullable protocol)
/**
* 返回該類的協(xié)議列表(結(jié)構(gòu)體中的objc_protocol_list)
*/
Protocol * * class_copyProtocolList(Class _Nullable cls, unsigned int * _Nullable outCount)
/**
* 返回該類的指定屬性(注意不是成員變量)
*/
objc_property_t class_getProperty(Class _Nullable cls, const char * _Nonnull name)
/**
* 返回該類的屬性列表
*/
objc_property_t * class_copyPropertyList(Class _Nullable cls, unsigned int * _Nullable outCount)
/**
* 返回該類成員變量的布局
*/
uint8_t * class_getIvarLayout(Class _Nullable cls)
/**
* 返回該類weak成員變量的布局
*/
uint8_t * class_getWeakIvarLayout(Class _Nullable cls)
/**
* 添加方法 如果返回YES則添加成功
*/
BOOL class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
const char * _Nullable types)
/**
* 替換方法實(shí)現(xiàn)(IMP)
* 如果之前存在該方法--直接替換
* 如果之前不存在該方法--添加方法
*/
IMP class_replaceMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
const char * _Nullable types)
/**
* 添加成員變量
* 這個函數(shù)只能在objc_allocateClassPair和objc_registerClassPair之前調(diào)用杂穷。
* 也間接的說明悍缠、已經(jīng)注冊的類不能動態(tài)的添加成員變量
*/
BOOL class_addIvar(Class _Nullable cls, const char * _Nonnull name, size_t size,
uint8_t alignment, const char * _Nullable types)
/**
* 添加協(xié)議
*/
BOOL class_addProtocol(Class _Nullable cls, Protocol * _Nonnull protocol)
/**
* 添加屬性
* 注意這里屬性的意義。和Category中的一樣亭畜、并不包含成員變量和setget方法
*/
BOOL class_addProperty(Class _Nullable cls, const char * _Nonnull name,
const objc_property_attribute_t * _Nullable attributes,
unsigned int attributeCount)
/**
* 替換屬性
* 注意這里屬性的意義扮休。和Category中的一樣、并不包含成員變量和setget方法
*/
void class_replaceProperty(Class _Nullable cls, const char * _Nonnull name,
const objc_property_attribute_t * _Nullable attributes,
unsigned int attributeCount)
/**
* 設(shè)置變量布局
*/
void class_setIvarLayout(Class _Nullable cls, const uint8_t * _Nullable layout)
/**
* 設(shè)置weak變量布局
*/
void class_setWeakIvarLayout(Class _Nullable cls, const uint8_t * _Nullable layout)
/**
* 由CoreFoundation的toll-free橋接使用拴鸵。
* 不能主動調(diào)用
*/
Class objc_getFutureClass(const char * _Nonnull name)
其中有比較特殊的:
-
class_getSuperclass
與[self superclass]
class_getSuperclass
等價于[self superclass]
都直接獲取指向父類對象的指針。
并且由之前的結(jié)構(gòu)圖(類關(guān)系示意圖)可知
1蜗搔、元類的元類為NSObject
2劲藐、元類的父類為正常結(jié)構(gòu)的父類
可以驗(yàn)證一下
NSLog(@"類對象--%@",[self class]);
NSLog(@"父類對象--%@",[self superclass]);
NSLog(@"父類對象--%@",[[self superclass] superclass]);
NSLog(@"父類對象--%@",[[[self superclass] superclass] superclass]);
NSLog(@"父類對象--%@",[[[[self superclass] superclass] superclass] superclass]);
//獲取self的類 ==》 ViewController(類) 再獲取其元類 ViewController(元類)
Class class = object_getClass(object_getClass(self));
NSLog(@"元類對象--%@",class);
NSLog(@"如果再繼續(xù)獲取元類---%@",object_getClass(class));
NSLog(@"父元類對象--%@",class_getSuperclass(class));
NSLog(@"父元類對象--%@",class_getSuperclass(class_getSuperclass(class)));
NSLog(@"父元類對象--%@",class_getSuperclass(class_getSuperclass(class_getSuperclass(class))));
NSLog(@"父類對象--%@",class_getSuperclass(class_getSuperclass(class_getSuperclass(class_getSuperclass(class)))));
NSLog(@"父類對象--%@",class_getSuperclass(class_getSuperclass(class_getSuperclass(class_getSuperclass(class_getSuperclass(class))))));
打印結(jié)果:
類對象--ViewController
父類對象--UIViewController
父類對象--UIResponder
父類對象--NSObject
父類對象--(null)
元類對象--ViewController
如果再繼續(xù)獲取元類---NSObject
父元類對象--UIViewController
父元類對象--UIResponder
父元類對象--NSObject
父類對象--NSObject
父類對象--(null)
動態(tài)創(chuàng)建類和對象
-
動態(tài)創(chuàng)建類
/**
* 創(chuàng)建一個新的類、以及其元類
*/
Class objc_allocateClassPair(Class _Nullable superclass, const char * _Nonnull name,
size_t extraBytes)
/**
* 注冊一個類
*/
void objc_registerClassPair(Class _Nonnull cls)
/**
* 用于KVO
* 禁止主動調(diào)用
*/
Class objc_duplicateClass(Class _Nonnull original, const char * _Nonnull name,
size_t extraBytes)
/**
* 銷毀一個類
* 如果程序運(yùn)行中還存在類或其子類的實(shí)例樟凄,則不能調(diào)用針對類調(diào)用該方法
*/
void objc_disposeClassPair(Class _Nonnull cls)
objc_allocateClassPair函數(shù):如果我們要創(chuàng)建一個根類聘芜,則superclass指定為Nil。extraBytes通常指定為0缝龄,該參數(shù)是分配給類和元類對象尾部的索引ivars的字節(jié)數(shù)汰现。
為了創(chuàng)建一個新類,我們需要調(diào)用objc_allocateClassPair
叔壤。
然后使用諸如class_addMethod瞎饲,class_addIvar等函數(shù)來為新創(chuàng)建的類添加方法、實(shí)例變量和屬性等炼绘。
完成這些后嗅战,我們需要調(diào)用objc_registerClassPair
函數(shù)來注冊類,之后這個新類就可以在程序中使用了。
有兩點(diǎn)需要注意:
- 實(shí)例方法和實(shí)例變量應(yīng)該添加到類自身上驮捍,而類方法應(yīng)該添加到類的元類上疟呐。
-
class_addIvar
必須使用在類生成后objc_allocateClassPair
、注冊前objc_registerClassPair
东且。因?yàn)槌绦虿辉试S一個已經(jīng)注冊启具、分配好內(nèi)存的類進(jìn)行擴(kuò)容。
※※※※這也是為什么類別珊泳、添加了屬性卻不能添加成員變量的原因
舉個例子:
Class cls = objc_allocateClassPair(MyClass.class, "MySubClass", 0);
class_addMethod(cls, @selector(submethod1), (IMP)imp_submethod1, "v@:");
class_replaceMethod(cls, @selector(method1), (IMP)imp_submethod1, "v@:");
class_addIvar(cls, "_ivar1", sizeof(NSString *), log(sizeof(NSString *)), "i");
objc_property_attribute_t type = {"T", "@\"NSString\""};
objc_property_attribute_t ownership = { "C", "" };
objc_property_attribute_t backingivar = { "V", "_ivar1"};
objc_property_attribute_t attrs[] = {type, ownership, backingivar};
class_addProperty(cls, "property2", attrs, 3);
objc_registerClassPair(cls);
id instance = [[cls alloc] init];
[instance performSelector:@selector(submethod1)];
[instance performSelector:@selector(method1)];
-
動態(tài)創(chuàng)建對象
/**
* 實(shí)例化一個類對象
* 基本等價于[XXX new]
* 第二個參數(shù)表示分配的額外字節(jié)數(shù)富纸。這些額外的字節(jié)可用于存儲在類定義中所定義的實(shí)例變量之外的實(shí)例變量。
*/
id class_createInstance(Class _Nullable cls, size_t extraBytes)
/**
* 在指定位置實(shí)例化一個類對象
*/
id objc_constructInstance(Class _Nullable cls, void * _Nullable bytes)
/**
* 銷毀一個類的實(shí)例
* 但不會釋放并移除任何與其相關(guān)的引用
*/
void * _Nullable objc_destructInstance(id _Nullable obj)
方法操作(method_
)
大概我們應(yīng)該先了解一下Method
struct objc_method {
SEL _Nonnull method_name; //方法名旨椒、借此才能從方法列表鎖定單個Method
char * _Nullable method_types; //方法返回值以及參數(shù)的描述
IMP _Nonnull method_imp; //方法實(shí)現(xiàn).(方法實(shí)現(xiàn)所在的位置)
}
Method
包含了一個方法所需要的全部要素晓褪。并且可以自由獲取任意其一。
/**
* 返回方法的名稱
*/
SEL method_getName(Method _Nonnull m)
/**
* 返回方法的實(shí)現(xiàn)(IMP)
*/
IMP method_getImplementation(Method _Nonnull m)
/**
* 返回描述方法參數(shù)和返回類型的字符串
*/
char * method_getTypeEncoding(Method _Nonnull m)
/**
* 返回方法參數(shù)個數(shù)
* 參數(shù)最低為兩個(每個方法都有self以及_cmd兩個隱性參數(shù))
*/
int method_getNumberOfArguments(Method _Nonnull m)
/**
* 返回方法返回類型
* 類型會傳遞給第二個參數(shù)"dst"
*/
void method_getReturnType(Method _Nonnull m, char * _Nonnull dst, size_t dst_len)
/**
* 返回方法返回類型
* 第二個參數(shù)index為參數(shù)位置综慎、類型會傳遞給第三個參數(shù)"dst"(越位會返回'\0')
*/
void method_getArgumentType(Method _Nonnull m, unsigned int index,
char * _Nullable dst, size_t dst_len)
/**
* 為一個Method設(shè)置具體實(shí)現(xiàn)(IMP)
*/
IMP method_setImplementation(Method _Nonnull m, IMP _Nonnull imp)
/**
* ※※※大名鼎鼎的swizzle※※※
*/
void method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2)
能說的不多:
-
swizzle的本質(zhì)?
method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2)
的注釋里說很明白了.
* IMP imp1 = method_getImplementation(m1);
* IMP imp2 = method_getImplementation(m2);
* method_setImplementation(m1, imp2);
* method_setImplementation(m2, imp1);
本質(zhì)就是交換了兩個方法的實(shí)現(xiàn)涣仿。
所以說用method_setImplementation
設(shè)置IMP的話、也可以實(shí)現(xiàn)一個IMP實(shí)現(xiàn)對應(yīng)多個SEL示惊。
實(shí)例變量操作(ivar_
)
和Method
一樣好港。我們也來看看ivar
的結(jié)構(gòu)體、看看ivar
能做什么米罚。
/// An opaque type that represents an instance variable.
typedef struct objc_ivar *Ivar;
struct objc_ivar {
char * _Nullable ivar_name; //變量名
char * _Nullable ivar_type; //變量類型
int ivar_offset //基地址偏移字節(jié)
#ifdef __LP64__
int space ; //占用空間
#endif
}
實(shí)例變量操作的API特別少(因?yàn)楂@取/設(shè)置變量的值都由ojbect_
進(jìn)行)
/**
* 返回實(shí)例變量的名稱
*/
char * _Nullable ivar_getName(Ivar _Nonnull v)
/**
* 返回實(shí)例變量的類型
*/
char * _Nullable ivar_getTypeEncoding(Ivar _Nonnull v)
/**
* 返回實(shí)例變量的偏移量
*/
ptrdiff_t ivar_getOffset(Ivar _Nonnull v)
- 基地址偏移字節(jié)
ivar_offset
- 先聊聊類的布局
在編譯我們的類時钧汹、編譯器生成了一個 ivar布局、顯示了在類中從哪可以訪問我們的 ivars 录择。如下圖:
我們增加了父類的ivar拔莱、這個時候布局就出錯了。
雖然runtime可以在類加載的時候通過Non Fragile ivars
進(jìn)行重新布局隘竭。
- 先聊聊類的布局
但是L燎亍!动看!尊剔、對已經(jīng)加載完成(注冊好)的類、增加成員變量一樣會導(dǎo)致布局出錯菱皆。
這也是為什么類別不能添加屬性的根本原因须误。
-
通過偏移量
ivar_offset
、獲取/修改任意一個成員變量仇轻。
我們可以通過對象地址 + ivar偏移字節(jié)
的方法京痢、獲取任意一個成員變量。
@interface Person : NSObject
{
@private int age_int;
@private NSString * age_str;
@public int age_public;
}
@end
@implementation Person
- (instancetype)init
{
self = [super init];
if (self) {
age_public = 1;
age_int = 1;
age_str = @"1";
}
return self;
}
- (NSString *)description
{
NSLog(@"person pointer==%p", self);
NSLog(@"age_int pointer==%p", &age_int);
return [NSString stringWithFormat:@"age_int==%d", age_int];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
//私有成員變量--基本類型拯田、可以使用偏移量
Person * person = [Person new];
Ivar age_int_ivar = class_getInstanceVariable(object_getClass(person), "age_int");
int *age_int_pointer = (int *)((__bridge void *)(person) + ivar_getOffset(age_int_ivar));
NSLog(@"age_int offset==%td", ivar_getOffset(age_int_ivar));
* age_int_pointer = 10;
NSLog(@"%@",person);
}
return 0;
}
打印結(jié)果:
age_int offset==8
person pointer==0x10061b070
age_int pointer==0x10061b078
age_int==10
屬性操作(property_
)
/**
* 返回屬性名
*/
char * _Nonnull property_getName(objc_property_t _Nonnull property)
/**
* 返回屬性描述(也就是屬性的屬性)
* @property (nonatomic ,readonly ,strong) NSObject * obj;
* "T@\"NSObject\",R,N,V_obj"
*/
char * _Nullable property_getAttributes(objc_property_t _Nonnull property)
/**
* 返回屬性描述數(shù)組
*/
objc_property_attribute_t * _Nullable
property_copyAttributeList(objc_property_t _Nonnull property,
unsigned int * _Nullable outCount)
/**
* 返回屬性描述(也就是屬性的屬性)
*/
char * _Nullable property_copyAttributeValue(objc_property_t _Nonnull property,
const char * _Nonnull attributeName)
- 屬性的動態(tài)添加與獲取
注意這里的添加和category一樣历造、有名無實(shí)。
objc_property_attribute_t type = { "T", "@\"NSString\"" };//屬性類型為NSString
objc_property_attribute_t ownership = { "C", "copy" }; // C = copy
objc_property_attribute_t backingivar = { "V", "_littleName" };//_littleName為Person類的全局變量,這里是讓新屬性與之關(guān)聯(lián)
objc_property_attribute_t attrs[] = { type, ownership, backingivar };
class_addProperty([Person class], "littleName", attrs, 3);
//驗(yàn)證是否添加成功
objc_property_t pt = class_getProperty([Person class], "littleName");
NSLog(@"property's name: %s", property_getName(pt));
NSLog(@"property'attribute:%s",property_getAttributes(pt));
NSLog(@"property'release:%s",property_copyAttributeValue(pt, "C"));
NSLog(@"property'type:%s",property_copyAttributeValue(pt, "T"));
NSLog(@"property'value:%s",property_copyAttributeValue(pt, "V"));
打印結(jié)果:
property's name: littleName
property'attribute:T@"NSString",Ccopy,V_littleName
property'release:copy
property'type:@"NSString"
property'value:_littleName
協(xié)議操作(protocol_
)
/**
* 返回一個協(xié)議
*/
Protocol * _Nullable objc_getProtocol(const char * _Nonnull name)
/**
* 返回runtime已知的所有協(xié)議的數(shù)組
*/
Protocol ** _Nullable objc_copyProtocolList(unsigned int * _Nullable outCount)
/**
* 判斷一個協(xié)議是否遵循了另一個協(xié)議
*/
BOOL protocol_conformsToProtocol(Protocol * _Nullable proto,
Protocol * _Nullable other)
/**
* 判斷兩個協(xié)議是否相同
*/
BOOL protocol_isEqual(Protocol * _Nullable proto, Protocol * _Nullable other)
/**
* 返回協(xié)議的名稱
*/
char * _Nonnull protocol_getName(Protocol * _Nonnull proto)
/**
* 返回協(xié)議中指定方法的描述
*/
struct objc_method_description protocol_getMethodDescription(Protocol * _Nonnull proto, SEL _Nonnull aSel,
BOOL isRequiredMethod, BOOL isInstanceMethod)
/**
* 返回協(xié)議中指定規(guī)則的所有方法描述
*/
struct objc_method_description * _Nullable protocol_copyMethodDescriptionList(Protocol * _Nonnull proto,
BOOL isRequiredMethod,
BOOL isInstanceMethod,
unsigned int * _Nullable outCount)
/**
* 返回協(xié)議的指定屬性
*/
objc_property_t protocol_getProperty(Protocol * _Nonnull proto,
const char * _Nonnull name,
BOOL isRequiredProperty, BOOL isInstanceProperty)
/**
* 返回協(xié)議中所有屬性的列表
*/
objc_property_t * protocol_copyPropertyList(Protocol * _Nonnull proto,
unsigned int * _Nullable outCount)
/**
* 返回協(xié)議中所有指定規(guī)則屬性的列表
*/
objc_property_t * protocol_copyPropertyList2(Protocol * _Nonnull proto,
unsigned int * _Nullable outCount,
BOOL isRequiredProperty, BOOL isInstanceProperty)
/**
* 返回協(xié)議所遵循的協(xié)議列表(比如`NSPortDelegate`遵循了`NSObject`)
*/
Protocol ** protocol_copyProtocolList(Protocol * _Nonnull proto,
unsigned int * _Nullable outCount)
/**
* 創(chuàng)建一個協(xié)議(未注冊)
*/
Protocol * objc_allocateProtocol(const char * _Nonnull name)
/**
* 向協(xié)議中添加方法
*/
void protocol_addMethodDescription(Protocol * _Nonnull proto, SEL _Nonnull name,
const char * _Nullable types,
BOOL isRequiredMethod, BOOL isInstanceMethod)
/**
* 向協(xié)議中添加依賴協(xié)議
*/
void protocol_addProtocol(Protocol * _Nonnull proto, Protocol * _Nonnull addition)
/**
* 向協(xié)議中添屬性
*/
void protocol_addProperty(Protocol * _Nonnull proto, const char * _Nonnull name,
const objc_property_attribute_t * _Nullable attributes,
unsigned int attributeCount,
BOOL isRequiredProperty, BOOL isInstanceProperty)
/**
* 注冊一個協(xié)議
*/
void objc_registerProtocol(Protocol * _Nonnull proto)
- 動態(tài)的創(chuàng)建一個協(xié)議
Protocol * protocol = objc_getProtocol("KTDelegate");
NSLog(@"%@",protocol);
if (!protocol) {
//創(chuàng)建
protocol = objc_allocateProtocol("KTDelegate");
protocol_addMethodDescription(protocol, @selector(protocol_func), "desc", YES, YES);
protocol_addProtocol(protocol, objc_getProtocol("NSObject"));
objc_property_attribute_t type = { "T", "@\"NSString\"" };//屬性類型為NSString
objc_property_attribute_t ownership = { "C", "copy" }; // C = copy
objc_property_attribute_t backingivar = { "V", "_littleName" };
objc_property_attribute_t attrs[] = { type, ownership, backingivar };
protocol_addProperty(protocol, "_littleName", attrs, 3, YES, YES);
//注冊
objc_registerProtocol(protocol);
NSLog(@"register_end");
}
protocol = objc_getProtocol("KTDelegate");
NSLog(@"protocol == %@",protocol);
NSLog(@"protocol_name == %s",protocol_getName(protocol));
NSLog(@"protocol_desc == %s",protocol_getMethodDescription(protocol, @selector(protocol_func), YES, YES));
NSLog(@"protocol_property == %s",property_getName(protocol_getProperty(protocol, "_littleName", YES, YES)));
打印結(jié)果:
(null)
register_end
protocol == <Protocol: 0x100462f80>
protocol_name == KTDelegate
protocol_desc == protocol_func
protocol_property == _littleName
庫操作
/**
* 返回所有加載在Objective-C框架和動態(tài)庫上的名稱
* char ** arr = objc_copyImageNames(&num);
*/
char ** objc_copyImageNames(unsigned int * _Nullable outCount)
/**
* 返回一個類所屬的動態(tài)庫名稱
*/
char * class_getImageName(Class _Nullable cls)
/**
* 返回一個庫中所有類的名稱
*/
char ** objc_copyClassNamesForImage(const char * _Nonnull image,
unsigned int * _Nullable outCount)
簡單舉個例子
char * libName = class_getImageName(objc_getClass("NSString"));
int num = 0;
char ** libarr = objc_copyClassNamesForImage(libName, &num);
for (int i = 0; i < num; i ++) {
NSLog(@"%s",libarr[i]);
}
選擇器操作(sel_
)
/**
* 返回一個選擇器所指定的方法名稱
*/
char * sel_getName(SEL _Nonnull sel)
/**
* 注冊一個方法選擇器
*/
SEL sel_registerName(const char * _Nonnull str)
/**
* 比較兩個選擇器
* SEL sel1 = @selector(fun1:);
* SEL sel2 = sel_registerName("fun1:");
* BOOL b = sel_isEqual(sel1, sel2);
*/
BOOL sel_isEqual(SEL _Nonnull lhs, SEL _Nonnull rhs)
語言特性
/**
* 當(dāng)一個obj突變發(fā)生時吭产,這個函數(shù)會拋出異常
*/
void objc_enumerationMutation(id _Nonnull obj)
/**
* 突變處理回調(diào)
*/
void objc_setEnumerationMutationHandler(void (*_Nullable handler)(id _Nonnull ))
/**
* objc_msgForward方法的調(diào)用
*/
void objc_setForwardHandler(void * _Nonnull fwd, void * _Nonnull fwd_stret)
/**
* 就是直接把一個block作為IMP
*/
IMP imp_implementationWithBlock(id _Nonnull block)
/**
* 返回(使用imp_implementationWithBlock創(chuàng)建的)IMP對應(yīng)的block
* 舉個例子:
void (^block)(id ,SEL) = ^(id self ,SEL _sel){
NSLog(@"%s",sel_getName(_sel));
};
IMP imp = imp_implementationWithBlock(block);
class_addMethod([Person class], @selector(imp_fun), imp, "v@:@");
[[Person new] performSelector:@selector(imp_fun)];
void (^block2)(id ,SEL)= imp_getBlock(imp);
block2([Person new],@selector(fun_block2));
輸出:
imp_fun
fun_block2
*/
id imp_getBlock(IMP _Nonnull anImp);
/**
* 移除(使用imp_implementationWithBlock創(chuàng)建的)IMP對應(yīng)的block
*/
BOOL imp_removeBlock(IMP _Nonnull anImp)
/**
* 在表達(dá)式中使用__weak變量時將自動使用該函數(shù)
* 該函數(shù)加載一個弱指針引用的對象侣监,并在對其做retain和autoreleasing操作后返回它。這樣臣淤,對象就可以在調(diào)用者使用它時保持足夠長的生命周期
UIView * view = [UIView new];
__weak UIView * w_view = view;
NSLog(@"retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(w_view)));
objc_loadWeak(&w_view);
NSLog(@"retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(w_view)));
__strong UIView * s_view = w_view;
__weak UIView * ww_view = w_view;
NSLog(@"retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(w_view)));
*/
id objc_loadWeak(id _Nullable * _Nonnull location)
/**
* 可以用來為__weak變量賦值
*/
id objc_storeWeak(id _Nullable * _Nonnull location, id _Nullable obj)
關(guān)聯(lián)策略
// 關(guān)聯(lián)策略
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
OBJC_ASSOCIATION_ASSIGN = 0, //@property(assign)橄霉。
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, //@property(strong, nonatomic)
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, //@property(copy, nonatomic)
OBJC_ASSOCIATION_RETAIN = 01401, //@property(strong,atomic)
OBJC_ASSOCIATION_COPY = 01403 //@property(copy, atomic)
};
/**
* 將一個key&&value關(guān)聯(lián)到另一個對象上、并且設(shè)置關(guān)聯(lián)策略
*/
void objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
id _Nullable value, objc_AssociationPolicy policy)
/**
* 將關(guān)聯(lián)的對象取出
*/
id _Nullable objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
/**
* 移除關(guān)聯(lián)所有對象
*/
void objc_removeAssociatedObjects(id _Nonnull object)
這個就是我們經(jīng)常用的邑蒋、給類別綁定屬性的
static char *key = "key";
objc_setAssociatedObject(self, &key, @"hello world", OBJC_ASSOCIATION_COPY_NONATOMIC);
NSString *associated = objc_getAssociatedObject(self, &key);
NSLog(@"objc_getAssociatedObject===%@",associated);
編碼類型
#define _C_ID '@' // 代表對象類型
#define _C_CLASS '#' // 代表類對象 (Class)
#define _C_SEL ':' // 代表方法selector (SEL)
#define _C_CHR 'c' // 代表char類型
#define _C_UCHR 'C' // 代表unsigned char類型
#define _C_SHT 's' // 代表short類型
#define _C_USHT 'S' // 代表unsigned short類型
#define _C_INT 'i' // 代表int類型
#define _C_UINT 'I' // 代表unsigned int類型
#define _C_LNG 'l' // 代表long類型姓蜂,在64位處理器上也是按照32位處理
#define _C_ULNG 'L' // 代表unsigned long類型
#define _C_LNG_LNG 'q' // 代表long long類型
#define _C_ULNG_LNG 'Q' // 代表unsigned long long類型
#define _C_FLT 'f' // 代表float類型
#define _C_DBL 'd' // 代表double類型
#define _C_BFLD 'b' //
#define _C_BOOL 'B' // 代表C++中的bool或者C99中的_Bool
#define _C_VOID 'v' // 代表void類型
#define _C_UNDEF '?' // 代表未知類型
#define _C_PTR '^' // 代表指針類型
#define _C_CHARPTR '*' // 代表字符串類型 (char *)
#define _C_ATOM '%' //
#define _C_ARY_B '[' // 代表array
#define _C_ARY_E ']'
#define _C_UNION_B '(' // 代表UNION
#define _C_UNION_E ')'
#define _C_STRUCT_B '{' // 代表結(jié)構(gòu)體
#define _C_STRUCT_E '}'
#define _C_VECTOR '!' // 代表矢量類型
#define _C_CONST 'r' // 代表常量類型
最后
本文主要是自己的學(xué)習(xí)與總結(jié)。如果文內(nèi)存在紕漏医吊、萬望留言斧正钱慢。如果不吝賜教小弟更加感謝。