收 集 文 章 / 超 人
文章出處
Rumtime方法說明
獲取類的類名
//如果傳入的cls為Nil,則返回一個字字符串。
const char * class_getName ( Class cls );
獲取類的父類
Class class_getSuperclass ( Class cls );
- class_getSuperclass函數,當cls為Nil或者cls為根類時,返回Nil。不過通常我們可以使用NSObject類的superclass方法來達到同樣的目的
判斷給定的Class是否是一個元類
//如果是cls是元類,則返回YES;如果否或者傳入的cls為Nil,則返回NO。
BOOL class_isMetaClass ( Class cls );
獲取實例大小
size_t class_getInstanceSize ( Class cls );
成員變量(ivars)及屬性
獲取類中指定名稱實例成員變量的信息
//返回一個指向包含name指定的成員變量信息的objc_ivar結構體的指針(Ivar)
Ivar class_getInstanceVariable ( Class cls, const char *name );
獲取類成員變量的信息
Ivar class_getClassVariable ( Class cls, const char *name );
- 目前沒有找到關于Objective-C中類變量的信息,一般認為Objective-C不支持類變量。注意,返回的列表不包含父類的成員變量和屬性。
添加成員變量
BOOL class_addIvar ( Class cls, const char *name, size_t size, uint8_t alignment, const char *types );
- Objective-C不支持往已存在的類中添加實例變量,因此不管是系統庫提供的提供的類,還是我們自定義的類,都無法動態添加成員變量。但如果我們通過運行時來創建一個類的話,又應該如何給它添加成員變量呢?這時我們就可以使用class_addIvar函數了。不過需要注意的是,這個方法只能在objc_allocateClassPair函數與objc_registerClassPair之間調用。另外,這個類也不能是元類。成員變量的按字節最小對齊量是1<
獲取整個成員變量列表
Ivar * class_copyIvarList ( Class cls, unsigned int *outCount );
- 返回一個指向成員變量信息的數組,數組中每個元素是指向該成員變量信息的objc_ivar結構體的指針。這個數組不包含在父類中聲明的變量。outCount指針返回數組的大小。需要注意的是,我們必須使用free()來釋放這個數組。
獲取指定的屬性
objc_property_t class_getProperty ( Class cls, const char *name );
獲取屬性列表
objc_property_t * class_copyPropertyList ( Class cls, unsigned int *outCount );
- 獲取的是所有@property定義屬性
為類添加屬性
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 );
方法(methodLists)
添加方法
BOOL class_addMethod ( Class cls, SEL name, IMP imp, const char *types );
- class_addMethod的實現會覆蓋父類的方法實現,但不會取代本類中已存在的實現,如果本類中包含一個同名的實現,則函數會返回NO。如果要修改已存在實現,可以使用method_setImplementation。一個Objective-C方法是一個簡單的C函數,它至少包含兩個參數—self和_cmd。所以,我們的實現函數(IMP參數指向的函數)至少需要兩個參數,如下所示:
void myMethodIMP(
id self
,SEL _cmd
)
{
// implementation ....
}
獲取實例方法
Method class_getInstanceMethod ( Class cls, SEL name );
獲取類方法
Method class_getClassMethod ( Class cls, SEL name );
獲取所有方法的數組
Method * class_copyMethodList ( Class cls, unsigned int *outCount );
- class_getInstanceMethod、class_getClassMethod函數,與class_copyMethodList不同的是,這兩個函數都會去搜索父類的實現。
- 返回包含所有實例方法的數組,如果需要獲取類方法,則可以使用class_copyMethodList(object_getClass(cls), &count)(一個類的實例方法是定義在元類里面)。該列表不包含父類實現的方法。outCount參數返回方法的個數。在獲取到列表后,我們需要使用free()方法來釋放它。
替代方法的實現
IMP class_replaceMethod ( Class cls, SEL name, IMP imp, const char *types );
- 該函數的行為可以分為兩種:如果類中不存在name指定的方法,則類似于class_addMethod函數一樣會添加方法;如果類中已存在name指定的方法,則類似于method_setImplementation一樣替代原方法的實現。
返回方法的具體實現
IMP class_getMethodImplementation ( Class cls, SEL name );
IMP class_getMethodImplementation_stret ( Class cls, SEL name );
- 該函數在向類實例發送消息時會被調用,并返回一個指向方法實現函數的指針。這個函數會比method_getImplementation(class_getInstanceMethod(cls, name))更快。返回的函數指針可能是一個指向runtime內部的函數,而不一定是方法的實際實現。例如,如果類實例無法響應selector,則返回的函數指針將是運行時消息轉發機制的一部分。
類實例是否響應指定的selector
BOOL class_respondsToSelector ( Class cls, SEL sel );
- 我們通常使用NSObject類的respondsToSelector:或instancesRespondToSelector:方法來達到相同目的。
協議(objc_protocol_list)
添加協議
BOOL class_addProtocol ( Class cls, Protocol *protocol );
返回類是否實現指定的協議
//可以使用NSObject類的conformsToProtocol:方法來替代。
BOOL class_conformsToProtocol ( Class cls, Protocol *protocol );
返回類實現的協議列表
//返回的是一個數組,在使用后我們需要使用free()手動釋放。
Protocol * class_copyProtocolList ( Class cls, unsigned int *outCount );
版本(version)
獲取版本號
int class_getVersion ( Class cls );
設置版本號
void class_setVersion ( Class cls, int version );
實例(Example)
//-----------------------------------------------------------
// MyClass.h
@interface MyClass : NSObject @property (nonatomic, strong) NSArray *array;
@property (nonatomic, copy) NSString *string;
- (void)method1;
- (void)method2;
+ (void)classMethod1;
@end
//-----------------------------------------------------------
// MyClass.m
#import "MyClass.h"
@interface MyClass () {
NSInteger _instance1;
NSString * _instance2;
}
@property (nonatomic, assign) NSUInteger integer;
- (void)method3WithArg1:(NSInteger)arg1 arg2:(NSString *)arg2;
@end
@implementation MyClass
+ (void)classMethod1 {
}
- (void)method1 {
NSLog(@"call method method1");
}
- (void)method2 {
}
- (void)method3WithArg1:(NSInteger)arg1 arg2:(NSString *)arg2 {
NSLog(@"arg1 : %ld, arg2 : %@", arg1, arg2);
}
@end
//-----------------------------------------------------------
// main.h
#import "MyClass.h"
#import "MySubClass.h"
#import int main(int argc, const char * argv[]) {
@autoreleasepool {
MyClass *myClass = [[MyClass alloc] init];
unsigned int outCount = 0;
Class cls = myClass.class;
// 類名
NSLog(@"class name: %s", class_getName(cls));
NSLog(@"==========================================================");
// 父類
NSLog(@"super class name: %s", class_getName(class_getSuperclass(cls)));
NSLog(@"==========================================================");
// 是否是元類
NSLog(@"MyClass is %@ a meta-class", (class_isMetaClass(cls) ? @"" : @"not"));
NSLog(@"==========================================================");
Class meta_class = objc_getMetaClass(class_getName(cls));
NSLog(@"%s's meta-class is %s", class_getName(cls), class_getName(meta_class));
NSLog(@"==========================================================");
// 變量實例大小
NSLog(@"instance size: %zu", class_getInstanceSize(cls));
NSLog(@"==========================================================");
// 成員變量
Ivar *ivars = class_copyIvarList(cls, &outCount);
for (int i = 0; i < outCount; i++) {
Ivar ivar = ivars[i];
NSLog(@"instance variable's name: %s at index: %d", ivar_getName(ivar), i);
}
free(ivars);
Ivar string = class_getInstanceVariable(cls, "_string");
if (string != NULL) {
NSLog(@"instace variable %s", ivar_getName(string));
}
NSLog(@"==========================================================");
// 屬性操作
objc_property_t * properties = class_copyPropertyList(cls, &outCount);
for (int i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
NSLog(@"property's name: %s", property_getName(property));
}
free(properties);
objc_property_t array = class_getProperty(cls, "array");
if (array != NULL) {
NSLog(@"property %s", property_getName(array));
}
NSLog(@"==========================================================");
// 方法操作
Method *methods = class_copyMethodList(cls, &outCount);
for (int i = 0; i < outCount; i++) {
Method method = methods[i];
NSLog(@"method's signature: %s", method_getName(method));
}
free(methods);
Method method1 = class_getInstanceMethod(cls, @selector(method1));
if (method1 != NULL) {
NSLog(@"method %s", method_getName(method1));
}
Method classMethod = class_getClassMethod(cls, @selector(classMethod1));
if (classMethod != NULL) {
NSLog(@"class method : %s", method_getName(classMethod));
}
NSLog(@"MyClass is%@ responsd to selector: method3WithArg1:arg2:", class_respondsToSelector(cls, @selector(method3WithArg1:arg2:)) ? @"" : @" not");
IMP imp = class_getMethodImplementation(cls, @selector(method1));
imp();
NSLog(@"==========================================================");
// 協議
Protocol * __unsafe_unretained * protocols = class_copyProtocolList(cls, &outCount);
Protocol * protocol;
for (int i = 0; i < outCount; i++) {
protocol = protocols[i];
NSLog(@"protocol name: %s", protocol_getName(protocol));
}
NSLog(@"MyClass is%@ responsed to protocol %s", class_conformsToProtocol(cls, protocol) ? @"" : @" not", protocol_getName(protocol));
NSLog(@"==========================================================");
}
return 0;
}
- 輸出結果
2014-10-22 19:41:37.452 RuntimeTest[3189:156810] class name: MyClass
2014-10-22 19:41:37.453 RuntimeTest[3189:156810] ==========================================================
2014-10-22 19:41:37.454 RuntimeTest[3189:156810] super class name: NSObject
2014-10-22 19:41:37.454 RuntimeTest[3189:156810] ==========================================================
2014-10-22 19:41:37.454 RuntimeTest[3189:156810] MyClass is not a meta-class
2014-10-22 19:41:37.454 RuntimeTest[3189:156810] ==========================================================
2014-10-22 19:41:37.454 RuntimeTest[3189:156810] MyClass's meta-class is MyClass
2014-10-22 19:41:37.455 RuntimeTest[3189:156810] ==========================================================
2014-10-22 19:41:37.455 RuntimeTest[3189:156810] instance size: 48
2014-10-22 19:41:37.455 RuntimeTest[3189:156810] ==========================================================
2014-10-22 19:41:37.455 RuntimeTest[3189:156810] instance variable's name: _instance1 at index: 0
2014-10-22 19:41:37.455 RuntimeTest[3189:156810] instance variable's name: _instance2 at index: 1
2014-10-22 19:41:37.455 RuntimeTest[3189:156810] instance variable's name: _array at index: 2
2014-10-22 19:41:37.455 RuntimeTest[3189:156810] instance variable's name: _string at index: 3
2014-10-22 19:41:37.463 RuntimeTest[3189:156810] instance variable's name: _integer at index: 4
2014-10-22 19:41:37.463 RuntimeTest[3189:156810] instace variable _string
2014-10-22 19:41:37.463 RuntimeTest[3189:156810] ==========================================================
2014-10-22 19:41:37.463 RuntimeTest[3189:156810] property's name: array
2014-10-22 19:41:37.463 RuntimeTest[3189:156810] property's name: string
2014-10-22 19:41:37.464 RuntimeTest[3189:156810] property's name: integer
2014-10-22 19:41:37.464 RuntimeTest[3189:156810] property array
2014-10-22 19:41:37.464 RuntimeTest[3189:156810] ==========================================================
2014-10-22 19:41:37.464 RuntimeTest[3189:156810] method's signature: method1
2014-10-22 19:41:37.464 RuntimeTest[3189:156810] method's signature: method2
2014-10-22 19:41:37.464 RuntimeTest[3189:156810] method's signature: method3WithArg1:arg2:
2014-10-22 19:41:37.465 RuntimeTest[3189:156810] method's signature: integer
2014-10-22 19:41:37.465 RuntimeTest[3189:156810] method's signature: setInteger:
2014-10-22 19:41:37.465 RuntimeTest[3189:156810] method's signature: array
2014-10-22 19:41:37.465 RuntimeTest[3189:156810] method's signature: string
2014-10-22 19:41:37.465 RuntimeTest[3189:156810] method's signature: setString:
2014-10-22 19:41:37.465 RuntimeTest[3189:156810] method's signature: setArray:
2014-10-22 19:41:37.466 RuntimeTest[3189:156810] method's signature: .cxx_destruct
2014-10-22 19:41:37.466 RuntimeTest[3189:156810] method method1
2014-10-22 19:41:37.466 RuntimeTest[3189:156810] class method : classMethod1
2014-10-22 19:41:37.466 RuntimeTest[3189:156810] MyClass is responsd to selector: method3WithArg1:arg2:
2014-10-22 19:41:37.467 RuntimeTest[3189:156810] call method method1
2014-10-22 19:41:37.467 RuntimeTest[3189:156810] ==========================================================
2014-10-22 19:41:37.467 RuntimeTest[3189:156810] protocol name: NSCopying
2014-10-22 19:41:37.467 RuntimeTest[3189:156810] protocol name: NSCoding
2014-10-22 19:41:37.467 RuntimeTest[3189:156810] MyClass is responsed to protocol NSCoding
2014-10-22 19:41:37.468 RuntimeTest[3189:156810] ==========================================================
動態創建類
創建一個新類和元類
Class objc_allocateClassPair ( Class superclass, const char *name, size_t extraBytes );
如果我們要創建一個根類,則superclass指定為Nil。extraBytes通常指定為0,該參數是分配給類和元類對象尾部的索引ivars的字節數。
為了創建一個新類,我們需要調用objc_allocateClassPair。然后使用諸如class_addMethod,class_addIvar等函數來為新創建的類添加方法、實例變量和屬性等。完成這些后,我們需要調用objc_registerClassPair函數來注冊類,之后這個新類就可以在程序中使用了。
實例方法和實例變量應該添加到類自身上,而類方法應該添加到類的元類上。
銷毀一個類及其相關聯的類
void objc_disposeClassPair ( Class cls );
- 用于銷毀一個類,不過需要注意的是,如果程序運行中還存在類或其子類的實例,則不能調用針對類調用該方法。
在應用中注冊由objc_allocateClassPair創建的類
void objc_registerClassPair ( Class cls );
實例(Example)
Class cls = objc_allocateClassPair(MyClass.class, "MySubClass", 0);
class_addMethod(cls, @selector(submethod1), (IMP)imp_submethod1, "v@:");
class_replaceMethod(cls, @selector(method1), (IMP)imp_submethod1, "v@:");
class_addIvar(cls, "_ivar1", sizeof(NSString *), log(sizeof(NSString *)), "i");
objc_property_attribute_t type = {"T", "@\"NSString\""};
objc_property_attribute_t ownership = { "C", "" };
objc_property_attribute_t backingivar = { "V", "_ivar1"};
objc_property_attribute_t attrs[] = {type, ownership, backingivar};
class_addProperty(cls, "property2", attrs, 3);
objc_registerClassPair(cls);
id instance = [[cls alloc] init];
[instance performSelector:@selector(submethod1)];
[instance performSelector:@selector(method1)];
- 輸出結果
2014-10-23 11:35:31.006 RuntimeTest[3800:66152] run sub method 1
2014-10-23 11:35:31.006 RuntimeTest[3800:66152] run sub method 1
動態創建對象
創建類實例
id class_createInstance ( Class cls, size_t extraBytes );
- 創建實例時,會在默認的內存區域為類分配內存。extraBytes參數表示分配的額外字節數。這些額外的字節可用于存儲在類定義中所定義的實例變量之外的實例變量。該函數在ARC環境下無法使用。
調用class_createInstance的效果與+alloc方法類似。不過在使用class_createInstance時,我們需要確切的知道我們要用它來做什么。在下面的例子中,我們用NSString來測試一下該函數的實際效果:
id theObject = class_createInstance(NSString.class, sizeof(unsigned));
id str1 = [theObject init];
NSLog(@"%@", [str1 class]);
id str2 = [[NSString alloc] initWithString:@"test"];
NSLog(@"%@", [str2 class]);
- 輸出日志
2014-10-23 12:46:50.781 RuntimeTest[4039:89088] NSString
2014-10-23 12:46:50.781 RuntimeTest[4039:89088] __NSCFConstantString
- 可以看到,使用class_createInstance函數獲取的是NSString實例,而不是類簇中的默認占位符類__NSCFConstantString。
在指定的位置(bytes)創建類實例
id objc_constructInstance ( Class cls, void *bytes );
銷毀類實例
void * objc_destructInstance ( id obj );
- 銷毀一個類的實例,但不會釋放并移除任何與其相關的引用
實例操作函數
實例操作函數主要是針對我們創建的實例對象的一系列操作函數,我們可以使用這組函數來從實例對象中獲取我們想要的一些信息,如實例對象中變量的值
返回指定對象的一份拷貝
id object_copy ( id obj, size_t size );
釋放指定對象占用的內存
id object_dispose ( id obj );
- 有這樣一種場景,假設我們有類A和類B,且類B是類A的子類。類B通過添加一些額外的屬性來擴展類A。現在我們創建了一個A類的實例對象,并希望在運行時將這個對象轉換為B類的實例對象,這樣可以添加數據到B類的屬性中。這種情況下,我們沒有辦法直接轉換,因為B類的實例會比A類的實例更大,沒有足夠的空間來放置對象。此時,我們就要以使用以上幾個函數來處理這種情況,如下代碼所示:
NSObject *a = [[NSObject alloc] init];
id newB = object_copy(a, class_getInstanceSize(MyClass.class));
object_setClass(newB, MyClass.class);
object_dispose(a);
修改類實例的實例變量的值
Ivar object_setInstanceVariable ( id obj, const char *name, void *value );
獲取對象實例變量的值
Ivar object_getInstanceVariable ( id obj, const char *name, void **outValue );
返回指向給定對象分配的任何額外字節的指針
void * object_getIndexedIvars ( id obj );
返回對象中實例變量的值
id object_getIvar ( id obj, Ivar ivar );
設置對象中實例變量的值
void object_setIvar ( id obj, Ivar ivar, id value );
- 如果實例變量的Ivar已經知道,那么調用object_getIvar會比object_getInstanceVariable函數快,相同情況下,object_setIvar也比object_setInstanceVariable快
返回給定對象的類名
const char * object_getClassName ( id obj );
返回對象的類
Class object_getClass ( id obj );
設置對象的類
Class object_setClass ( id obj, Class cls );
獲取類定義
Objective-C動態運行庫會自動注冊我們代碼中定義的所有的類。我們也可以在運行時創建類定義并使用objc_addClass函數來注冊它們。runtime提供了一系列函數來獲取類定義相關的信息
獲取已注冊的類定義的列表
int objc_getClassList ( Class *buffer, int bufferCount );
- objc_getClassList函數:獲取已注冊的類定義的列表。我們不能假設從該函數中獲取的類對象是繼承自NSObject體系的,所以在這些類上調用方法是,都應該先檢測一下這個方法是否在這個類中實現。
創建并返回一個指向所有已注冊類的指針列表
Class * objc_copyClassList ( unsigned int *outCount );
返回指定類的類定義
Class objc_lookUpClass ( const char *name );
Class objc_getClass ( const char *name );
Class objc_getRequiredClass ( const char *name );
返回指定類的元類
Class objc_getMetaClass ( const char *name );
- 如果指定的類沒有注冊,則該函數會調用類處理回調,并再次確認類是否注冊,如果確認未注冊,再返回nil。不過,每個類定義都必須有一個有效的元類定義,所以這個函數總是會返回一個元類定義,不管它是否有效。
實例(Example)
int numClasses;
Class * classes = NULL;
numClasses = objc_getClassList(NULL, 0);
if (numClasses > 0) {
classes = malloc(sizeof(Class) * numClasses);
numClasses = objc_getClassList(classes, numClasses);
NSLog(@"number of classes: %d", numClasses);
for (int i = 0; i < numClasses; i++) {
Class cls = classes[i];
NSLog(@"class name: %s", class_getName(cls));
}
free(classes);
}
- 輸出日志
2014-10-23 16:20:52.589 RuntimeTest[8437:188589] number of classes: 1282
2014-10-23 16:20:52.589 RuntimeTest[8437:188589] class name: DDTokenRegexp
2014-10-23 16:20:52.590 RuntimeTest[8437:188589] class name: _NSMostCommonKoreanCharsKeySet
2014-10-23 16:20:52.590 RuntimeTest[8437:188589] class name: OS_xpc_dictionary
2014-10-23 16:20:52.590 RuntimeTest[8437:188589] class name: NSFileCoordinator
2014-10-23 16:20:52.590 RuntimeTest[8437:188589] class name: NSAssertionHandler
2014-10-23 16:20:52.590 RuntimeTest[8437:188589] class name: PFUbiquityTransactionLogMigrator
2014-10-23 16:20:52.591 RuntimeTest[8437:188589] class name: NSNotification
2014-10-23 16:20:52.591 RuntimeTest[8437:188589] class name: NSKeyValueNilSetEnumerator
2014-10-23 16:20:52.591 RuntimeTest[8437:188589] class name: OS_tcp_connection_tls_session
2014-10-23 16:20:52.591 RuntimeTest[8437:188589] class name: _PFRoutines
......還有大量輸出
- 獲取類定義的方法有三個:objc_lookUpClass, objc_getClass和objc_getRequiredClass。如果類在運行時未注冊,則objc_lookUpClass會返回nil,而objc_getClass會調用類處理回調,并再次確認類是否注冊,如果確認未注冊,再返回nil。而objc_getRequiredClass函數的操作與objc_getClass相同,只不過如果沒有找到類,則會殺死進程.