大家知道OC是一門動態(tài)語言,他將一些靜態(tài)語言以及庫的鏈接的一些事情放在運行時來進行處理,這就給我們增加了編程的靈活性,比如可以隨意交換兩個方法的執(zhí)行,消息的轉(zhuǎn)發(fā)等等。這就是Runtime運行時。這個庫全部由C語言來編寫的,該對象用結(jié)構(gòu)體來表示,方法使用C函數(shù)來表示。當這些結(jié)構(gòu)體以及方法用runtime函數(shù)來修飾封裝后,我們就可以對類和對象進行創(chuàng)建、檢查、修改等操作了。
1、基本結(jié)構(gòu)
/// An opaque type that rePResents a method in a class definition.類中定義的方法
typedefstruct objc_method *Method;
/// An opaque type that represents an instance variable.類中定義的實例變量
typedefstruct objc_ivar *Ivar;
/// An opaque type that represents a category.類別
typedefstruct objc_category *Category;
/// An opaque type that represents an Objective-C declared property.類中聲明的屬性
typedefstruct objc_property *objc_property_t;
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY; isa指針,通過它可以找到對象所述的類,isa指針在代碼運行時并不總是指向?qū)嵗鶎俚念?,因此不能通過其獲取類型,但是可以通過‘’-class來確定。KVO的實現(xiàn)機理就是通過isa指針指向一個中間類而不是真實類來實現(xiàn)的。
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
constchar *name OBJC2_UNAVAILABLE; ------類名
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; ------屬性列表
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;------方法列表
struct objc_cache *cache OBJC2_UNAVAILABLE;------緩存,一種優(yōu)化機制
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;------協(xié)議列表
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
2、常用的獲取列表的屬性unsigned int count; //獲取屬性列表 objc_property_t *propertyList = class_copyPropertyList([self class], &count); for (unsigned int i=0; i<count; i++) { const char *propertyName = property_getName(propertyList[i]); NSLog(@"property---->%@", [NSString stringWithUTF8String:propertyName]); } //獲取方法列表 Method *methodList = class_copyMethodList([self class], &count); for (unsigned int i; i<count; i++) { Method method = methodList[i]; NSLog(@"method---->%@", NSStringFromSelector(method_getName(method))); } //獲取成員變量列表 Ivar *ivarList = class_copyIvarList([self class], &count); for (unsigned int i; i<count; i++) { Ivar myIvar = ivarList[i]; const char *ivarName = ivar_getName(myIvar); NSLog(@"Ivar---->%@", [NSString stringWithUTF8String:ivarName]); } //獲取協(xié)議列表 __unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count); for (unsigned int i; i<count; i++) { Protocol *myProtocal = protocolList[i]; const char *protocolName = protocol_getName(myProtocal); NSLog(@"protocol---->%@", [NSString stringWithUTF8String:protocolName]); }3、消息轉(zhuǎn)發(fā)在OC中調(diào)用方法一般是如下調(diào)用
id returnValue = [someObject messageName:parameter];,someObject是消息的接受者,messageName是一個選擇器,parameter則為參數(shù)。選擇器+參數(shù) 就是我們所稱為的消息。
其在底層中是將我們的消息轉(zhuǎn)換為標準的C語言函數(shù),Void objc_msgSend(id self , SEL cmd , .....) ,self 為消息接收者,cmd為選擇器,省略號為參數(shù),表示可變長度參數(shù)。因此以上的消息方法會轉(zhuǎn)換為id returnValue = objc_msgSend( someObject , @selector(messageName:) , parameter);
之所以O(shè)bjc_msgsend方法總能找到正確的函數(shù)去執(zhí)行,原因是OC中每個類都有一張方法的列表存儲這個類的所有方法,當發(fā)出objc_msgsend時,會根據(jù)object的isa指針找到類結(jié)構(gòu)的方法,如果找不到則會到父類尋找該方法的實現(xiàn),直到NSObject類。上面有提到cach緩存機制,蘋果為了加快尋找速率,runtime 系統(tǒng)會緩存使用過的SEL 和方法地址。
通過object的isa指針找到他的類結(jié)構(gòu)class在在相應(yīng)操作的對象中的緩存方法列表中找調(diào)用的方法,如果找到,轉(zhuǎn)向相應(yīng)實現(xiàn)并執(zhí)行(IMP)如果沒找到,在相應(yīng)操作的對象中的方法列表中找調(diào)用的方法,如果找到,轉(zhuǎn)向相應(yīng)實現(xiàn)執(zhí)行(IMP)如果還未找到則會去父類中重復(fù)上面操作尋找如果最終還是未找到,則會調(diào)用實例方法調(diào)用+(bool)resolveInstanceMethod:(SEL)sel ;類方法調(diào)用:+(bool)resolveClassMethod:(SEL)sel;如果返回為YES,則會重啟一次消息發(fā)送過程,調(diào)用你自己添加的方法。+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel ==@selector(sendMessage:)) {
class_addMethod([selfclass], sel,imp_implementationWithBlock(^(idself,NSString *Word) {
NSLog(@"method resolution way : send message = %@", word);
}), "v@*");
}
returnYES;
}
- (void)sendMessage:(NSString *)word
{
NSLog(@"normal way : send message = %@", word);
}
如果返回NO,則會調(diào)用- (id)forwardingTargetForSelector:(SEL)aSelector方法,再次方法中可以將你調(diào)用的不存在的方法重定向到一個其他聲明了這個方法的類,只需要你返回一個有這個方法的target,- (id)forwardingTargetForSelector:(SEL)aSelector
{
if (aSelector ==@selector(sendMessage:)) {
return [MessageForwardingnew];//新建的類,見下
}
returnnil;
}
#import <Foundation/Foundation.h>
@interface MessageForwarding :NSObject
- (void)sendMessage:(NSString *)word;
@end
如果return nil,則會- (void)forwardInvocation:(NSInvocation *)anInvocation將你調(diào)用的不存在的方法打包成NSInvocation傳給你。做完你自己的處理后,調(diào)用invokeWithTarget:方法讓某個target觸發(fā)這個方法。- (void)forwardInvocation:(NSInvocation *)anInvocation
{
MessageForwarding *messageForwarding = [MessageForwardingnew];
if ([messageForwardingrespondsToSelector:anInvocation.selector]) {
[anInvocationinvokeWithTarget:messageForwarding];
}
}
#import <Foundation/Foundation.h>
@interface MessageForwarding :NSObject
- (void)sendMessage:(NSString *)word;
@end
4、runtime實際應(yīng)用更改私有屬性 、給category添加屬性、歸檔解檔、AOP等等,具體事例請看本博客其他文章
新聞熱點
疑難解答