什么是Runtime
Objective-C語言是一門動態(tài)語言,它將很多靜態(tài)語言在編譯和鏈接時期做的事放到了運行時來處理。這種動態(tài)語言的優(yōu)勢在于:我們寫代碼時更具靈活性,如我們可以把消息轉發(fā)給我們想要的對象,或者隨意交換一個方法的實現(xiàn)等.
我們將C++和Objective進行對比,雖然C++和Objective-C都是在C的基礎上加入面向對象的特性擴充而成的程序設計語言,但二者實現(xiàn)的機制差異很大。C++是基于靜態(tài)類型,而Objective-C是基于動態(tài)運行時類型。也就是說用C++編寫的程序編譯時就直接編譯成了可令機器讀懂的機器語言;用Objective-C編寫的程序不能直接編譯成可令機器讀懂的機器語言,而是在程序運行的時候,通過Runtime把程序轉為可令機器讀懂的機器語言。Runtime是Objective不可缺少的重要一部分。
**傳送門->runtime源碼
類和對象
<pre>
/// An opaque type that represents an Objective-C class.
typedef struct objc_class Class;
typedef struct objc_object id;/// Represents an instance of a class.
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
if !OBJC2
Class super_class OBJC2_UNAVAILABLE;
const char *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;
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
</pre>
- Class是一個指向objc_class結構體的指針,而id是一個指向objc_object結構體的指針,其中的isa是一個指向objc_class結構體的指針。其中的id就是我們所說的對象,Class就是我們所說的類。
- 類與對象的區(qū)別就是類比對象多了很多特征成員,方法列表,父類,緩存,協(xié)議等信息。
- isa:objc_object(實例對象)中isa指針指向的類結構稱為class(也就是該對象所屬的類),其中存放著普通成員變量與動態(tài)方法(“-”開頭的方法);類對象中isa指針指向的類結構稱為metaclass,其中存放著static類型的成員變量與static類型的方法(“+”開頭的方法)。
- super_class: 指向該類的父類的指針,如果該類是根類(如NSObject或NSProxy),那么super_class就為nil。
-
類與對象的繼承層次關系如下圖所示,非常經(jīng)典的一張圖
類與對象的繼承層次關系圖
成員變量和屬性
- 定義
<pre>
//成員變量 Ivar
- typedef struct objc_ivar *Ivar;
objc_ivar的定義如下:
struct objc_ivar {
char *ivar_name OBJC2_UNAVAILABLE; // 變量名
char *ivar_type OBJC2_UNAVAILABLE; // 變量類型
int ivar_offset OBJC2_UNAVAILABLE; // ?基地址偏移字節(jié)
ifdef LP64
int space OBJC2_UNAVAILABLE; // 占用空間
endif
}
</pre>
<pre>
typedef struct objc_property *objc_property_t;
typedef struct {
const char *name; // 名稱
const char *value; // 值(通常是空的)
} objc_property_attribute_t;
</pre>
- 常用方法
<pre>
// 獲取類中指定名稱實例成員變量的信息
Ivar class_getInstanceVariable ( Class cls, const char *name );
// 獲取類成員變量的信息
Ivar class_getClassVariable ( Class cls, const char *name );
// 添加成員變量
BOOL class_addIvar ( Class cls, const char *name, size_t size, uint8_t alignment, const char *types );
// 獲取整個成員變量列表
Ivar * class_copyIvarList ( Class cls, unsigned int *outCount );
</pre>
<pre>
// 獲取指定的屬性
objc_property_t class_getProperty ( Class cls, const char *name );
// 獲取屬性列表
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 );
</pre>
方法(SEL,IMP,METHOD)
Method代表類中的某個方法的類型
<pre>
typedef struct objc_method *Method;
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE; // 方法名
char *method_types OBJC2_UNAVAILABLE; // 方法類型
IMP method_imp OBJC2_UNAVAILABLE; // 方法實現(xiàn)
}
</pre>Sel 代表方法名
<pre>
typedef struct objc_selector *SEL;
struct objc_selector {
*name; OBJC2_UNAVAILABLE;// 名稱
char *types; OBJC2_UNAVAILABLE;// 類型
};
</pre>IMP 表示方法實現(xiàn),實際是一個函數(shù)指針
<pre>
typedef id (*IMP)(id, SEL, ...);
</pre>常用方法
<pre>
// 添加類方法
BOOL class_addMethod ( Class cls, SEL name, IMP imp, const char *types );
// 獲取實例方法
Method class_getInstanceMethod ( Class cls, SEL name );
// 獲取類方法
Method class_getClassMethod ( Class cls, SEL name );
// 獲取所有方法的數(shù)組
Method * class_copyMethodList ( Class cls, unsigned int *outCount );
// 替代方法的實現(xiàn)
IMP class_replaceMethod ( Class cls, SEL name, IMP imp, const char *types );
// 返回方法的具體實現(xiàn)
IMP class_getMethodImplementation ( Class cls, SEL name );
IMP class_getMethodImplementation_stret ( Class cls, SEL name );
// 類實例是否響應指定的selector
BOOL class_respondsToSelector ( Class cls, SEL sel );
</pre>
除了以上類型外,還有objc_protocol_list,cache,version等
上面基本介紹了runtime中類和對象的基本知識,為了加深印象,這里做一個例子,手動創(chuàng)建一個類,添加方法并直接調用,遍歷其中的成員變量和屬性
runtime實踐
<pre>
/**
- 創(chuàng)建一個方法,并用兩種方法獲取成員變量的值
- @param self <#self description#>
- @param _cmd <#_cmd description#>
- @param text <#text description#>
*/
void sayHelloFunction(id self,SEL _cmd,id text)
{
NSLog(@"%@個輪子的%@%@",object_getIvar(self, class_getInstanceVariable([self class], "_num")),[self valueForKey:@"_name"],text);
}
void test_runtime_class()
{
//動態(tài)創(chuàng)建一個繼承自NSObject的Car類
Class Car = objc_allocateClassPair([NSObject class], "Car", 0);
//動態(tài)添加一個NSString *_name的成員變量
class_addIvar(Car, "_name", sizeof(NSString*), log2(sizeof(NSString*)), @encode(NSString*));
//動態(tài)添加一個int _num成員變量
class_addIvar(Car, "_num", sizeof(int), sizeof(int), @encode(int));
//注冊方法名為sayHello的方法名
SEL sel = sel_registerName("sayHello:");
//動態(tài)添加sayHello的方法
class_addMethod(Car, sel, (IMP)sayHelloFunction, "v@:@");
//注冊Car類
objc_registerClassPair(Car);
//創(chuàng)建Car的實例對象
id carInstance = [[Car alloc] init];
//從類中獲取成員變量,并為成員變量賦值
Ivar nameIvar = class_getInstanceVariable(Car, "_name");
object_setIvar(carInstance, nameIvar, @"法拉利??");
Ivar numIvar = class_getInstanceVariable(Car, "_num");
object_setIvar(carInstance, numIvar, @4);
//直接調用方法
((void (*)(id,SEL,id)) objc_msgSend)(carInstance,sel,@"跑起來了");
//銷毀類
objc_disposeClassPair(Car);
}
</pre>
<pre>
//
// People.m
// runtimeDemo
//
// Created by aaron on 16/6/5.
// Copyright ? 2016年 aaron. All rights reserved.
// 遍歷獲取類對象的成員變量和屬性列表
import "People.h"
import <objc/runtime.h>
@implementation People
-
(instancetype)initWithName:(NSString *)name reName:(NSString *)reName
{
self = [super init];
if (self) {
_name = name;
_reName = reName;
}return self;
} -
(NSDictionary *)allIvar
{
NSMutableDictionary *ivarDict = [NSMutableDictionary dictionary];unsigned int count;
Ivar *ivarList = class_copyIvarList([self class], &count);
for (int i = 0; i < count; i++) {Ivar ivar = ivarList[i]; NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)]; id value = [self valueForKey:ivarName]; if (value) { ivarDict[ivarName] = value; } else { ivarDict[ivarName] = [NSString stringWithFormat:@"key:%@對應的值為空",ivarName]; }
}
free(ivarList);return ivarDict;
} -
(NSDictionary *)allProperty
{
NSMutableDictionary *propertyDict = [NSMutableDictionary dictionary];unsigned int count;
objc_property_t *propertyList = class_copyPropertyList([self class], &count);
for (int i = 0; i < count; i++) {objc_property_t property = propertyList[i]; NSString *name = [NSString stringWithUTF8String:property_getName(property)]; id value = [self valueForKey:name]; if (value) { propertyDict[name] = value; } else { propertyDict[name] = [NSString stringWithFormat:@"key:%@對應的值為空",name]; }
}
free(propertyList);return propertyDict;
}
@end
</pre>