大家知道OC是一門動態語言,他將一些靜態語言以及庫的鏈接的一些事情放在運行時來進行處理,這就給我們增加了編程的靈活性,比如可以隨意交換兩個方法的執行,消息的轉發等等。這就是Runtime運行時。這個庫全部由C語言來編寫的,該對象用結構體來表示,方法使用C函數來表示。當這些結構體以及方法用runtime函數來修飾封裝后,我們就可以對類和對象進行創建、檢查、修改等操作了。
1、基本結構
/// 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指針在代碼運行時并不總是指向實例所屬的類,因此不能通過其獲取類型,但是可以通過‘’-class來確定。KVO的實現機理就是通過isa指針指向一個中間類而不是真實類來實現的。
#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;------緩存,一種優化機制
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;------協議列表
#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]); } //獲取協議列表 __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、消息轉發在OC中調用方法一般是如下調用
id returnValue = [someObject messageName:parameter];,someObject是消息的接受者,messageName是一個選擇器,parameter則為參數。選擇器+參數 就是我們所稱為的消息。
其在底層中是將我們的消息轉換為標準的C語言函數,Void objc_msgSend(id self , SEL cmd , .....) ,self 為消息接收者,cmd為選擇器,省略號為參數,表示可變長度參數。因此以上的消息方法會轉換為id returnValue = objc_msgSend( someObject , @selector(messageName:) , parameter);
之所以Objc_msgsend方法總能找到正確的函數去執行,原因是OC中每個類都有一張方法的列表存儲這個類的所有方法,當發出objc_msgsend時,會根據object的isa指針找到類結構的方法,如果找不到則會到父類尋找該方法的實現,直到NSObject類。上面有提到cach緩存機制,蘋果為了加快尋找速率,runtime 系統會緩存使用過的SEL 和方法地址。
通過object的isa指針找到他的類結構class在在相應操作的對象中的緩存方法列表中找調用的方法,如果找到,轉向相應實現并執行(IMP)如果沒找到,在相應操作的對象中的方法列表中找調用的方法,如果找到,轉向相應實現執行(IMP)如果還未找到則會去父類中重復上面操作尋找如果最終還是未找到,則會調用實例方法調用+(bool)resolveInstanceMethod:(SEL)sel ;類方法調用:+(bool)resolveClassMethod:(SEL)sel;如果返回為YES,則會重啟一次消息發送過程,調用你自己添加的方法。+ (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,則會調用- (id)forwardingTargetForSelector:(SEL)aSelector方法,再次方法中可以將你調用的不存在的方法重定向到一個其他聲明了這個方法的類,只需要你返回一個有這個方法的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將你調用的不存在的方法打包成NSInvocation
傳給你。做完你自己的處理后,調用invokeWithTarget:
方法讓某個target觸發這個方法。- (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實際應用更改私有屬性 、給category添加屬性、歸檔解檔、AOP等等,具體事例請看本博客其他文章
新聞熱點
疑難解答