Objective-C是一門動態性比較強的編程語言,跟C、C++等語言有着很大的不一樣
Objective-C的動態性是由Runtime API來支撐的
Runtime API提供的接口基本都是C語言的,源碼由C\C++\彙編語言編寫
複製代碼
在arm64架構以前,isa就是一個普通的指針,存儲着Class、Meta-Class對象的內存地址
從arm64架構開始,對isa進行了優化,變成了一個共用體(union)結構,還使用位域來存儲更多的信息
複製代碼
& 都是1纔是1, 一個1,就是0
0000&0010 能夠取出某一個特定位數值
掩碼: 用來按位與(&)運算的
1<<0 左移0位 則爲0b 0000 0001
1<<1 左移1位 則爲0b 0000 0010
1<<2 左移2位 則爲0b 0000 0400
1左移幾位
複製代碼
| 有1纔是1, 一個1,就是1,2個0就是0
0000 | 0010 結果就是 0010
複製代碼
~ 0000 1010 取反位1111 0101
複製代碼
struct{
char tall : 1; //表示只佔一位
char rich : 1;
char handsome: 1
} test // 0b0000 0111
複製代碼
unioc{
char bits;
struct{
char tall : 1;
char rich : 1;
char handsome: 1
} test
}
複製代碼
Class內部結構中有個方法緩存(cache_t),用散列表(哈希表)來緩存曾經調用過的方法,能夠提升方法的查找速度
複製代碼
緩存查找
objc-cache.mm
bucket_t * cache_t::find(cache_key_t k, id receiver)
複製代碼
objc_msgSend(對象,sel_registerName(方法名字字符串))
// 消息接收者(receiver)
// 消息名稱
// OC的方法調用: 消息機制,給方法調用者發送消息
內部執行分3大階段
1. 消息發送
2. 動態方法解析
3. 消息轉發
複製代碼
1.經過方法名字去 類對象 方法緩存中查找,若是有則返回方法地址
2.若是沒有緩存,則會遍歷 方法列表查找
3.查到了方法返回,並添加到 緩存列表
4.若是沒找到則會去父類緩存中查找,在去父類方法列表中查找,一層一層父類往上找
複製代碼
1.若是 消息發送 未找到方法,則會進行動態方法解析
2.若是是 對象方法調用會 調用_class_resoveInstanceMethod()
若是是 類方法調用 調用 _class_resoveClassMethod()
3.+(BOOL)ResoveInstanceMethod:(SEL) sel{
// 獲取其餘方法
Method method = class_getInstanceMethod(self,@selector(test));
//動態添加方法
class_addMethod(self,
@selecetor(test),
method_getImplementation(method),
method_getTypeEndcoing(method));
return YES;
}
類方法同理
複製代碼
動態添加方法 api
// 消息轉發-
-(id)forwardTargetForSelector:(SEL) aSelector{
// 判斷方法名字
if(aSelector == @selector(test)){
//轉發給哪一個對象解決
return [NSPerson alloc]init];
}
}
// 消息轉發- 方法簽名,返回值類型,參數類型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if(aSelector == @selector(testMethod))
{
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return nil;
}
-(void)forwardInvocation:(NSInvocation *)anInvocation
{
if (anInvocation.selector == @selector(testMethod))
{
TestModelHelper1 *h1 = [[TestModelHelper1 alloc] init];
TestModelHelper2 *h2 = [[TestModelHelper2 alloc] init];
[anInvocation invokeWithTarget:h1];
[anInvocation invokeWithTarget:h2];
}
}
複製代碼
[super message] 消息接受者仍是子類,方法尋找是從父類查找的
super調用,底層會轉換爲objc_msgSendSuper2函數的調用,接收2個參數
struct objc_super2
SEL
複製代碼
receiver是消息接收者
current_class是receiver的Class對象
複製代碼
利用Runtime遍歷全部的屬性或者成員變量
利用KVC設值
複製代碼
class_replaceMethod
method_exchangeImplementations
複製代碼
動態建立一個類(參數:父類,類名,額外的內存空間)
Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)
註冊一個類(要在類註冊以前添加成員變量)
void objc_registerClassPair(Class cls)
銷燬一個類
void objc_disposeClassPair(Class cls)
獲取isa指向的Class
Class object_getClass(id obj)
設置isa指向的Class
Class object_setClass(id obj, Class cls)
判斷一個OC對象是否爲Class
BOOL object_isClass(id obj)
判斷一個Class是否爲元類
BOOL class_isMetaClass(Class cls)
獲取父類
Class class_getSuperclass(Class cls)
複製代碼
獲取一個實例變量信息
Ivar class_getInstanceVariable(Class cls, const char *name)
拷貝實例變量列表(最後須要調用free釋放)
Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
設置和獲取成員變量的值
void object_setIvar(id obj, Ivar ivar, id value)
id object_getIvar(id obj, Ivar ivar)
動態添加成員變量(已經註冊的類是不能動態添加成員變量的)
BOOL class_addIvar(Class cls, const char * name, size_t size, uint8_t alignment, const char * types)
獲取成員變量的相關信息
const char *ivar_getName(Ivar v)
const char *ivar_getTypeEncoding(Ivar v)
複製代碼
獲取一個屬性
objc_property_t class_getProperty(Class cls, const char *name)
拷貝屬性列表(最後須要調用free釋放)
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
動態添加屬性
BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
unsigned int attributeCount)
動態替換屬性
void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
unsigned int attributeCount)
獲取屬性的一些信息
const char *property_getName(objc_property_t property)
const char *property_getAttributes(objc_property_t property)
複製代碼
####12.22.4 Runtime API04 – 方法緩存
得到一個實例方法、類方法
Method class_getInstanceMethod(Class cls, SEL name)
Method class_getClassMethod(Class cls, SEL name)
方法實現相關操做
IMP class_getMethodImplementation(Class cls, SEL name)
IMP method_setImplementation(Method m, IMP imp)
void method_exchangeImplementations(Method m1, Method m2)
拷貝方法列表(最後須要調用free釋放)
Method *class_copyMethodList(Class cls, unsigned int *outCount)
動態添加方法
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
動態替換方法
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
獲取方法的相關信息(帶有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)
選擇器相關
const char *sel_getName(SEL sel)
SEL sel_registerName(const char *str)
用block做爲方法實現
IMP imp_implementationWithBlock(id block)
id imp_getBlock(IMP anImp)
BOOL imp_removeBlock(IMP anImp)
複製代碼
運行循環-在程序運行中循環作一些事情性能優化
應用範疇
定時器(Timer)、PerformSelector
GCD Async Main Queue
事件響應、手勢識別、界面刷新
網絡請求
AutoreleasePool
複製代碼
RunLoop的基本做用
保持程序的持續運行
處理App中的各類事件(好比觸摸事件、定時器事件等)
節省CPU資源,提升程序性能:該作事時作事,該休息時休息
......
複製代碼
iOS中有2套API來訪問和使用RunLoop
Foundation:NSRunLoop
Core Foundation:CFRunLoopRef
NSRunLoop和CFRunLoopRef都表明着RunLoop對象
NSRunLoop是基於CFRunLoopRef的一層OC包裝
CFRunLoopRef是開源的
https://opensource.apple.com/tarballs/CF/
複製代碼
每條線程都有惟一的一個與之對應的RunLoop對象
RunLoop保存在一個全局的Dictionary裏,線程做爲key,RunLoop做爲value
線程剛建立時並無RunLoop對象,RunLoop會在第一次獲取它時建立
RunLoop會在線程結束時銷燬
主線程的RunLoop已經自動獲取(建立),子線程默認沒有開啓RunLoop
複製代碼
Foundation
[NSRunLoop currentRunLoop]; // 得到當前線程的RunLoop對象
[NSRunLoop mainRunLoop]; // 得到主線程的RunLoop對象
Core Foundation
CFRunLoopGetCurrent(); // 得到當前線程的RunLoop對象
CFRunLoopGetMain(); // 得到主線程的RunLoop對象
複製代碼
CFRunLoopModeRef表明RunLoop的運行模式
一個RunLoop包含若干個Mode,每一個Mode又包含若干個Source0/Source1/Timer/Observer
RunLoop啓動時只能選擇其中一個Mode,做爲currentMode
若是須要切換Mode,只能退出當前Loop,再從新選擇一個Mode進入
- 不一樣組的Source0/Source1/Timer/Observer能分隔開來,互不影響
若是Mode裏沒有任何Source0/Source1/Timer/Observer,RunLoop會立馬退出
複製代碼
常見的2種Mode
kCFRunLoopDefaultMode(NSDefaultRunLoopMode):App的默認Mode,一般主線程是在這個Mode下運行
UITrackingRunLoopMode:界面跟蹤 Mode,用於 ScrollView 追蹤觸摸滑動,保證界面滑動時不受其餘 Mode 影響
複製代碼
添加Observer監聽RunLoop的全部狀態bash
控制線程生命週期(線程保活)
解決NSTimer在滑動時中止工做的問題
監控應用卡頓
性能優化
複製代碼
runloop 若是沒有任何 source/source0 timer/observer 就會退出
複製代碼