Objective-C語言的許多決策可以在編譯和運行時執行。只要有可能,它是動態的。這意味著Objective-C語言不僅需要一個編譯器,還需要一個運行時系統來執行編譯的代碼。Runtime系統是一種用於Objective-C語言的操作系統,它使OC語言工作起來。
Runtime的核心是在運行時動態操作類和消息分發給其他對象,本文檔主要介紹在運行時動態操作類。
你可以從中學到運行時動態操作類。但是,多數情況下,我們並不需要編寫Runtime系統的相關代碼。
注意:在Runtime中,所有的char * 數據是UTF-8編碼。
#import < objc/runtime.h >
User.h
//
// User.h
// Runtime
//
// Created by yangjun on 15/9/21.
// Copyright © 2015年 六月. All rights reserved.
//
#import
/// 用戶
@interface User : NSObject
@property (nonatomic, copy) NSString *userName;///< 用戶名
/**
* 初始化
*
* @param userName 用戶名
*
* @return id
*/
- (id)initWithUserName:(NSString *)userName;
/**
* 初始化
*
* @param userName 用戶名
*
* @return id
*/
+ (id)userWithUserName:(NSString *)userName;
@end
User.m
//
// User.m
// Runtime
//
// Created by yangjun on 15/9/21.
// Copyright © 2015年 六月. All rights reserved.
//
#import User.h
@implementation User
+ (id)userWithUserName:(NSString *)userName
{
User *user = [[User alloc] init];
user.userName = userName;
return userName;
}
#pragma mark 初始化
- (id)initWithUserName:(NSString *)userName
{
self = [super init];
if (self) {
self.userName = userName;
}
return self;
}
@end
1.源代碼
#pragma mark - 類
- (void)testClass
{
// 獲取類
id userClass = objc_getClass(User);
NSLog(@%@, userClass);
// 等價
userClass = [User class];
NSLog(@%@, userClass);
// 父類
fprintf(stdout,
父類
);
id superUserClass = class_getSuperclass(userClass);
NSLog(@%@, superUserClass);
// 等價
superUserClass = [User new].superclass;
NSLog(@%@, superUserClass);
// 更改對象的類
fprintf(stdout,
更改對象的類
);
User *user = [User new];
NSLog(@類:%@ userName:%@, user, user.userName);
userClass = object_setClass(user, [ClassTest class]);// 類替換,並返回原來的類屬性
NSLog(@類:%@ userName:%@, user, user.userName);
}
2.輸出
2015-09-23 10:12:52.140 Runtime[14129:681188] User
2015-09-23 10:12:52.141 Runtime[14129:681188] User
父類
2015-09-23 10:12:52.141 Runtime[14129:681188] NSObject
2015-09-23 10:12:52.141 Runtime[14129:681188] NSObject
更改對象的類
2015-09-23 10:12:52.141 Runtime[14129:681188] 類: userName:(null)
2015-09-23 10:12:52.141 Runtime[14129:681188] 類: userName:陽君
1.源代碼
#pragma mark - 類名
- (void)testName{
// 獲取class
id userClass = objc_getClass(User);
const char *className = class_getName(userClass);
fprintf(stdout, 類名:%s
, className);
// 等價
className = object_getClassName(userClass);
fprintf(stdout, 類名:%s
, className);
// 等價OC
userClass = [User class];
NSString *name = NSStringFromClass(userClass);
NSLog(@類名:%@, name);
// 底層轉換
name = [NSString stringWithCString:className encoding:NSUTF8StringEncoding];
NSLog(@類名:%@, name);
}
2.輸出
類名:User
類名:User
2015-09-23 10:14:03.278 Runtime[14140:681770] 類名:User
2015-09-23 10:14:03.279 Runtime[14140:681770] 類名:User
1.源代碼
#pragma mark - 屬性
- (void)testPropertyName
{
// 獲取所有屬性
fprintf(stdout, 獲取所有屬性
);
id userClass = objc_getClass(User);
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(userClass, &outCount);// 所有屬性
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];// 屬性
const char *propertyName = property_getName(property);// 屬性名
const char *propertyAttributes = property_getAttributes(property); //屬性類型
fprintf(stdout, %s %s
, propertyName, propertyAttributes);
// 等價輸出
NSLog(@%@, [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding]);
}
// 單一屬性
fprintf(stdout,
單一屬性
);
Ivar var = class_getInstanceVariable(userClass, _userName);
const char *typeEncoding =ivar_getTypeEncoding(var);
const char *ivarName = ivar_getName(var);
fprintf(stdout, 屬性名:%s; 類型:%s
, ivarName, typeEncoding);
// 設置/獲取屬性值,需要關閉ARC模式
fprintf(stdout,
設置/獲取屬性值
);
User *user = [[[User alloc] init] autorelease];
NSString *userName = @陽君;
object_setInstanceVariable(user, _userName, userName);
NSLog(@user.userName:%@, user.userName);
user.userName = @開啟ARC;// 修改值
object_getInstanceVariable(user, _userName, (void*)&userName);
NSLog(@user.userName:%@, userName);
}
2.輸出
獲取所有屬性
userName T@”NSString”,C,N,V_userName
2015-09-23 10:18:54.365 Runtime[14155:684097] userName
單一屬性
屬性名:_userName; 類型:@”NSString”
設置/獲取屬性值
2015-09-23 10:18:54.366 Runtime[14155:684097] user.userName:陽君
2015-09-23 10:18:54.367 Runtime[14155:684097] user.userName:開啟ARC
1.源代碼
全局方法:
NSString *classAddMethodIMP(id self, SEL _cmd, NSString *str) {
// implementation ....
NSLog(@值:%@, str);
return str;
}
NSString *userName(id self, SEL _cmd) {
return @OC;
}
局部方法:
#pragma mark - 方法
- (void)testMethod
{
// 獲取所有方法
fprintf(stdout, 獲取所有方法
);
id userClass = objc_getClass(User);
u_int count;// unsigned int
Method *methods= class_copyMethodList(userClass, &count);// 所有方法,只包含實例方法)
for (int i = 0; i < count ; i++) {
Method method = methods[i];
SEL name = method_getName(method);// 轉為方法
const char *selName = sel_getName(name);// 轉為方法名
const char *methodTypeEncoding = method_getTypeEncoding(method);// 方法傳輸的參數
char *methodType = method_copyReturnType(method);// 方法返回的類型
fprintf(stdout, 方法名:%s; 返回類型%s; 參數:%s
, selName, methodType, methodTypeEncoding);
}
// 1.提取Method(類方法)
fprintf(stdout,
方法提取
);
SEL name = sel_registerName(userWithUserName:);
Method method = class_getClassMethod(userClass, name);
name = method_getName(method);
fprintf(stdout, 提取方法(+):%s
, sel_getName(name));
// 2.提取Method(實例方法)
name = sel_registerName(initWithUserName:);
method = class_getInstanceMethod(userClass, name);
name = method_getName(method);
fprintf(stdout, 提取方法(-):%s
, sel_getName(name));
// 3.提取IMP
IMP imp = method_getImplementation(method);
// 只能獲取(-)方法
imp = class_getMethodImplementation(userClass, name);
imp = class_getMethodImplementation_stret(userClass, name);
// 等價
User *user = [[User alloc] init];
imp = [user methodForSelector:name];
// 類增加方法(全局方法)
fprintf(stdout,
類增加方法
);
name = sel_registerName(classAddMethodIMP);
BOOL addMethod = class_addMethod(userClass, name, (IMP)classAddMethodIMP,i@:@);
// 判斷類是否有此方法
if (addMethod && class_respondsToSelector(userClass, name) && [user respondsToSelector:name]) {
fprintf(stdout, 類%s添加方法%s成功
, class_getName(userClass), sel_getName(name));
}
// 類添加方法(-)
name = @selector(classAddMethod:);
method = class_getInstanceMethod(self.class, name);// 方法體
imp = method_getImplementation(method);// 方法的實現
class_addMethod(userClass, name, imp, v@:);
// 方法調用
id methodBack = [user performSelector:@selector(classAddMethod:) withObject:@陽君];
NSLog(@類%@調用方法%@ 返回:%@, NSStringFromClass(user.class), [NSString stringWithCString:sel_getName(name) encoding:NSUTF8StringEncoding], methodBack);
// 方法交換
fprintf(stdout,
方法交換
);
Method m1 = class_getInstanceMethod([self class], @selector(userName));
Method m2 = class_getInstanceMethod([self class], @selector(userName2));
method_exchangeImplementations(m1, m2);
NSLog(@%@, [self userName]);
NSLog(@%@, [self userName2]);
// 方法替換
fprintf(stdout,
方法替換
);
Method userNameMethod = class_getInstanceMethod(self.class, @selector(userName));
IMP userNameImp = method_getImplementation(userNameMethod);
Method setUserNameMethod = class_getInstanceMethod(self.class, @selector(userName2));
method_setImplementation(setUserNameMethod, userNameImp);
NSLog(@%@, [self userName]);
NSLog(@%@, [self userName2]);
// 方法覆蓋,只能使用全局方法替換,方法名需要一致
fprintf(stdout,
方法覆蓋
);
NSLog(@%@, user.userName);
name = sel_registerName(userName);
imp = class_replaceMethod(userClass, name, (IMP)userName,i@:@);
NSLog(@%@, user.userName);
}
#pragma mark 覆蓋的方法
- (NSString *)userName
{
return @陽君;
}
- (NSString *)userName2
{
return @IOS;
}
#pragma mark 增加的方法
- (NSString *)classAddMethod:(NSString *)str
{
return str;
}
2.輸出
獲取所有方法
方法名:initWithUserName:; 返回類型@; 參數:@24@0:8@16
方法名:userName; 返回類型@; 參數:@16@0:8
方法名:setUserName:; 返回類型v; 參數:v24@0:8@16
方法提取
提取方法(+):userWithUserName:
提取方法(-):initWithUserName:
類增加方法
類User添加方法classAddMethodIMP成功
2015-09-23 10:21:19.167 Runtime[14182:685354] 類User調用方法classAddMethod: 返回:陽君
方法交換
2015-09-23 10:21:19.168 Runtime[14182:685354] IOS
2015-09-23 10:21:19.168 Runtime[14182:685354] 陽君
方法替換
2015-09-23 10:21:19.168 Runtime[14182:685354] IOS
2015-09-23 10:21:19.168 Runtime[14182:685354] IOS
方法覆蓋
2015-09-23 10:21:19.169 Runtime[14182:685354] (null)
2015-09-23 10:21:19.169 Runtime[14182:685354] OC