一.isa
isa的理解
- 在arm64架構(gòu)之前, isa就是一個(gè)普通的指針,存儲(chǔ)著Class、Meta-Class對(duì)象的內(nèi)存地址
- 從arm64架構(gòu)開始,對(duì)isa進(jìn)行了優(yōu)化,變成了一個(gè)共用體( union )結(jié)構(gòu),還使用位域來(lái)存儲(chǔ)更多的信息
使用64位(8個(gè)字節(jié))存儲(chǔ)了大量信息
源碼---右方數(shù)字表示占用的位數(shù)
union isa_ t
{
Class cls;
uintptr_ ,t bits;
struct (
uintptr_ t nonpointer : 1;
uintptr_t has_assoc: : 1;
uintptr_t has_cxX_ dtor : 1;
uintptr_t shiftcls : 33;
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_ rc : 1;
uintptr_t extra_rc : 19;
);
};
注意:shiftcls保存著類對(duì)象的地址值,占用33bit
位運(yùn)算---設(shè)值與取值:
取值用按位與& (0b0000 0001-取第八位數(shù)值)
設(shè)值用按位與&(0b1111 1110-設(shè)第八位為0) + 按位或|(0b0000 0001-設(shè)第八位為1):需要確保其他值不變贷洲,只修改想設(shè)置的值
(按位非 ~)
二.class結(jié)構(gòu)
cache緩存
Class內(nèi)部結(jié)構(gòu)中有個(gè)方法緩存( cache_t ) , 用散列表來(lái)緩存曾經(jīng)調(diào)用過(guò)的方法,可以提高方法的查找速度(空間換時(shí)間)
SEL --函數(shù)(方法)名
imp --指向函數(shù)的指針(函數(shù)地址)
用下面一張圖大概概括class的結(jié)構(gòu)
三.objc_msgSend 消息轉(zhuǎn)發(fā)
OC的方法調(diào)用:消息機(jī)制,給方法調(diào)用者發(fā)送消息
objc_msgsend的執(zhí)行流程可以分為3大階段
- 消息發(fā)送
- 動(dòng)態(tài)方法解析
兩個(gè)關(guān)鍵實(shí)現(xiàn)方法:+resolveInstanceMethod:/+resolveClassMethod:
在OC中調(diào)用某個(gè)方法缴啡,當(dāng)?shù)谝徊剑l(fā)送階段未找到此方法時(shí)瓷们,會(huì)進(jìn)入第二步---動(dòng)態(tài)方法解析
//動(dòng)態(tài)添加實(shí)例方法
- (void)other
{
NSLog(@"%s",__ func__ );
}
+ ( BOOL)resolveInstanceMethod: (SEL )sel
{
if(sel == @selector(test)) f
// 獲取其他方法
Method method = class_ getInstanceMethod(self, @selector(other));
//動(dòng)態(tài)添加test方法的突現(xiàn)
class_addMethod(self, sel,
method_getImplementation(method) ,
method_getTypeEncoding(method));
//返回YES代表有幼戀添加方法
return YES;
}
return [super resolveInstanceMethod:sel];
}
- 消息轉(zhuǎn)發(fā)
當(dāng)方法調(diào)用的第一步业栅,與第二步都沒(méi)有調(diào)用成功的時(shí)候。會(huì)進(jìn)入第三步--消息轉(zhuǎn)發(fā)谬晕,執(zhí)行方法 forwardingTargetForSelector
//當(dāng)進(jìn)入第三步時(shí)碘裕,會(huì)調(diào)用 MJCat 的test方法
- (id)forwardingTargetForSelector: (SEL)aSelector{
if (aSelector == @selector(test)) f
return [[MJCat alloc] init];
}
return [super forwardingTargetForSelector :aSelector];
}
擴(kuò)展
提醒編譯器不要自動(dòng)生成setter和getter的實(shí)現(xiàn)、不要自動(dòng)生成成員變量
@dynamic age;
四.[ self class]與[super class]
一.[ self class]的底層實(shí)現(xiàn)
- 1.objc_msgSend消息接收者仍然是子類對(duì)象
所以super class與self class 本質(zhì)是一樣的
-(Class) class{
return object_ getClass(self );
}
- 2.從父類開始查找方法的實(shí)現(xiàn)
二.[self superclass]的底層實(shí)現(xiàn)
superclass方法的返回值固蚤,也是取決于消息接收(調(diào)用)者是誰(shuí)
- (Class) superclass //獲取調(diào)用者的父類
{
return class_getSuperclass(object_getClass(self)); //獲取self對(duì)象的父類方法
}
因此 [self superclass]與[super superclass]的本質(zhì)一樣
同一個(gè)類中,下面兩個(gè)方法的打印結(jié)果是一樣的
NSLog(@"[self class] = %@", [self class]);
NSLog(@"'[ super class]= %@"[super class]);
NSLog (@"[ self superclass] = %@", [self superclass]) ;
NSLog (@"[ super superclass] = %@", [super superclass]) ;
五.isKindOfClass和isMemberOfClass
實(shí)例方法
-(BOOL)isKindOfClass:(Class)aClass;// 判斷左邊實(shí)例的類對(duì)象或其父類的類對(duì)象是否跟右邊類對(duì)象相等
-(BOOL)isMemberOfClass:(Class)aClass; // 判斷左邊實(shí)例的類對(duì)象是否跟右邊類對(duì)象相等
類方法
+(BOOL)isKindOfClass:(Class)aClass歹茶;//左邊類的類對(duì)象或父類的類對(duì)象(即元類)是否跟右邊類對(duì)象相等
+(BOOL)isMemberOfClass:(Class)aClass;//左邊類的類對(duì)象(即元類)是否跟右邊類對(duì)象相等
六.面試題
一.講一下OC的消息機(jī)制
- OC中的方法調(diào)用其實(shí)都是轉(zhuǎn)成了objc msgSend函數(shù)的調(diào)用,給receiver (方法調(diào)用者)發(fā)送了一條消息 ( selector方法名)
2.objc_ msgSend底層有3大階段
消息發(fā)送(當(dāng)前類夕玩、父類中查找)、動(dòng)態(tài)方法解析惊豺、消息轉(zhuǎn)發(fā)
二.什么是Runtime燎孟?平時(shí)項(xiàng)目中有用到過(guò)嗎?
OC是一門動(dòng)態(tài)性比較強(qiáng)的編程語(yǔ)言,允許很多操作推遲到程序運(yùn)行時(shí)再進(jìn)行
OC的動(dòng)態(tài)性就是由Runtime來(lái)支撐和實(shí)現(xiàn)的, Runtime是一 套C語(yǔ)言的API ,封裝了很多動(dòng)態(tài)性相關(guān)的函數(shù)
使用:
1.類相關(guān): 利用關(guān)聯(lián)對(duì)象尸昧,給分類添加屬性(成員變量)
2.成員變量:遍歷類的所有成員變量(修改textfield的占位文字顏色(修改成員變量值)揩页、字典轉(zhuǎn)模型(YYmodel的核心)、自動(dòng)歸檔解檔)
3.方法相關(guān):
1.方法交換(hook(鉤子)方法烹俗,最常用的應(yīng)用是防止數(shù)組爆侣、字典等越界崩潰).
2.利用消息發(fā)送機(jī)制進(jìn)行非常規(guī)的方法調(diào)用 objc_msgSend
方法交換實(shí)例:
+ (void)swizzleInstanceMethod2:(SEL)originalSel with:(SEL)swizzledSel {
Method originalMethod = class_getInstanceMethod(self, originalSel);
Method swizzledMethod = class_getInstanceMethod(self, swizzledSel);
NSLog(@"給當(dāng)前分類添加原類的方法originalSelector");
BOOL didAddMethod =
class_addMethod(self,
originalSel,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
NSLog(@"originalSelector方法在原類中不存在,已經(jīng)添加成功幢妄,用下面的方法替換其實(shí)現(xiàn)");
class_replaceMethod(self,
swizzledSel,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
NSLog(@"如果原類中已存在originalSelector的話兔仰,那么添加失敗而已,返回NO");
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
方法交換代碼解析
方法交換應(yīng)用場(chǎng)景//統(tǒng)計(jì)用戶進(jìn)入各個(gè)控制器的次數(shù)
- Runtime中類相關(guān)的API
//動(dòng)態(tài)創(chuàng)建-一個(gè)類(參數(shù):父類,類名,額外的內(nèi)存空間)
Class objc_allocateClassPair(Class superclass, const char *name, size_ _t extraBytes)
//注冊(cè)一個(gè)類(要在類注冊(cè)之前添加成員變量)
void objc_registerClassPair(Class cls)
//銷毀一個(gè)類
void objc_disposeClassPair(Class cls)
//獲取isa指向的Class
Class object_getClass(id obj)
//設(shè)置isa指向的Class
Class object_setClass(id obj, Class cls)
//判斷一個(gè)OC對(duì)象是否為Class
BOOL object_isClass(id obj)
//判斷一個(gè)Class是否為元類
BO0L class_isMetaClass(Class cls)
//獲取父類
Class class_getSuperclass(Class cls)
- Runtime中成員變量相關(guān)的API
//獲取一個(gè)實(shí)例變量
Ivar class_getInstanceVariable(Class cls, const char *name)
//拷貝實(shí)例變量列表(最后需要調(diào)用free釋放)
Ivar *Class_copyIvarList(Class cls, unsigned int *outCount)
//設(shè)置和獲取成員變量的值
void object_setIvar(id obj, Ivar ivar, id value)
id object_getIvar(id obj, Ivar ivar)
//動(dòng)態(tài)添加成員變量(已經(jīng)注冊(cè)的類是不能動(dòng)態(tài)添加成員變量的)
BOOL class_addIvar(Class cls, const char * name, size_t size, uint8_t alignment, const char * types)
//獲取成員變量的相關(guān)信息
const char *ivar_getName(Ivar v)
const char *ivar_getTypeEncoding(Ivar v)
- Runtime中方法相關(guān)的API
//獲得一個(gè)實(shí)例方法蕉鸳、類方法
Method class_ getInstanceMethod(Class cls, SEL name)
Method class_ _getClassMethod(Class cls, SEL name)
//方法實(shí)現(xiàn)相關(guān)操作
IMP class_ getMethodImplementation(Class cls, SEL name)
IMP method_ set Imp lementation (Method m, IMP imp)
void method_ exchangeImp lementat ions (Method m1, Method m2)
//拷貝方法列表(最后需要凋用free釋放)
Method *Class_ copyMethodList(Class cls, unsigned int *outCount)
//動(dòng)態(tài)添加方法
B00L class_ addMethod(Class cls, SEL name, IMP imp, const char *types)
//動(dòng)態(tài)替換方法
IMP class_ replaceMethod(Class cls, SEL name, IMP imp, const char *types)
//獲取方法的相關(guān)信息(帯有copy的需要凋用free去釋放)
SEL method_ getName (Method m)
IMP method_ getImplementation(Method m)
const char *method_getTypeEncoding (Method m)
unsigned int method_getNumberOfArguments (Method m)
char *method_copyReturnType (Method m)
char *method_copyArgumentType(Method m, unsigned int index)