来源
Objective-C 来源于Smalltalk,使用消息结构(message structure),运行时执行的代码由运行环境决定,这个过程叫做动态绑定。Swift则是函数调用(function calling),运行时执行的代码由编译器决定,从虚方法表查出需要执行的函数。
在C语言基础上增加了面相对象特性;如类为结构体指针,方法为函数指针。
Runtime是一套API,是一个层级,OC代码被编译成C语言,然后调用runtime进而执行程序。
所以我们可以通过runtime的API动态完成一些功能。
编译.m文件
通过终端命令编译.m 文件:clang -rewrite-objc xxx.m可以看到编译后的xxx.cpp(C++文件)
获取类名
class_getName
// Objective-C
const char *classChar = class_getName(ClassName.self);
NSString *className = [NSString stringWithUTF8String:classChar];
// Swift
let className = String(cString: class_getName(ClassName.self)
备注:
非runtime方法获取类名:
Objective-C
NSString *className = NSStringFromClass(ClassName.self)
Swift
let className = String(describing: ClassName.self)
注意:通过Objective-C方法(如class_getName、NSStringFromClass)获取 Swift class 得到的类名会有模块前缀。也可以理解为:Swift类在Objective-C中会有模块前缀
获取成员变量
class_copyIvarList
And ivar_getName
// Objective-C
- (void)fetchIvarList:(Class)aClass{
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList(aClass, &count);
for (int i = 0; i < count ; i++) {
const char *ivarName = ivar_getName(ivarList[i]);
const char *ivarType = ivar_getTypeEncoding(ivarList[i]);
NSLog(@"%@ ivar name = %@,type = %@",aClass,[NSString stringWithUTF8String:ivarName],[NSString stringWithUTF8String:ivarType]);
}
free(ivarList);
}
// Swift
func fetchIvarList(aClass: AnyClass) {
var count: UInt32 = 0
let ivarList = class_copyIvarList(aClass, &count)
for i in 0..<count {
let name = String(cString: ivar_getName(ivarList![Int(i)]))
let type = String(cString: ivar_getTypeEncoding(ivarList![Int(i)]))
print("\(aClass) ivar name = ",name, "type = ", type)
}
free(ivarList)
}
备注:
Swift中的
class_copyIvarList
也可以获取纯Swift类的成员变量。只可以获取当前类的成员变量,无法获取父类的成员变量。
获取属性列表
class_copyPropertyList
And property_getName
// Objective-C
- (void)fetchPropertyList:(Class)aClass{
unsigned int count = 0;
objc_property_t *propertyList = class_copyPropertyList(aClass, &count);
for (int i = 0; i < count ; i++) {
const char *proName = property_getName(propertyList[i]);
const char *proType = property_getAttributes(propertyList[i]);
NSLog(@"%@ property name = %@, type = %@",aClass, [NSString stringWithUTF8String:proName],[NSString stringWithUTF8String:proType]);
}
free(propertyList);
}
// Swift
func fetchPropertyList(aClass: AnyClass) {
var count: UInt32 = 0
let propertyList = class_copyPropertyList(aClass, &count)
for i in 0..<count {
let name = String(cString: property_getName(propertyList![Int(i)]))
let type = String(cString: property_getAttributes(propertyList![Int(i)]))
print("\(aClass) property = ",name, "type = ", type)
}
free(propertyList)
}
备注:
Swift类中的属性可以
class_copyIvarList
也可以通过class_copyPropertyList
获取到,可以说,Swift类中的属性,也同样是成员变量。
获取方法列表
class_copyMethodList
And method_getName
// Objective-C
- (void)fetchMethodList:(Class)aClass{
unsigned int count = 0;
Method *methodList = class_copyMethodList(aClass, &count);
for (int i = 0; i < count; i++) {
SEL methodName = method_getName(methodList[i]);
const char *name = sel_getName(methodName);
const char *methodType = method_getTypeEncoding(methodList[i]);
NSLog(@"%@ method name = %@, type = %@",aClass,[NSString stringWithUTF8String:name],[NSString stringWithUTF8String:methodType]);
}
free(methodList);
}
// Swift
func fetchMethodList(aClass: AnyClass) {
var count: UInt32 = 0
let methodList = class_copyMethodList(aClass, &count)
for i in 0..<count {
let sel = method_getName(methodList![Int(i)])
let name = String(cString: sel_getName(sel))
let type = String(cString: method_getTypeEncoding(methodList![Int(i)]))
print("\(aClass) methond = ",name, "type = ", type)
}
free(methodList)
}
备注:
继承NSObject的类,无论是Swift还是OC,都可以通过
class_copyMethodList
获取属性的set和get方法,也可以获取实例方法。如果Swift中方法前面添加了
fileprivate
关键词,则无法获取到。如果是纯的Swift类,则无法获取到实例方法。
纯Swift类或者添加了
fileprivate
关键词的实例方法无法获取到,添加关键词@objc
之后就可以获取到。@objc在Swift类中,为function/variable添加@objc以便可以在OC中使用;@objc 修饰符的另一个作用是为 Objective-C 侧重新声明方法或者变量的名字。另外,如果你用 Swift 写的 class 是继承自 NSObject 的话,Swift 会默认自动为所有的非 private 的类和成员加上 @objc。但是需要注意的是,添加 @objc 修饰符并不意味着这个方法或者属性会变成动态派发,Swift 依然可能会将其优化为静态调用。
方法交换
class_getInstanceMethod
And method_exchangeImplementations
// Objective-C
- (void)exchangeMethod:(Class)aClass method:(SEL)method1 otherMethod:(SEL)method2 {
Method m1 = class_getInstanceMethod(aClass, method1);
Method m2 = class_getInstanceMethod(aClass, method1);
method_exchangeImplementations(m1, m2);
}
// Swift
func exchangeMethod(withClass aClass:AnyClass, method1: Selector, method2: Selector) {
let m1 = class_getInstanceMethod(aClass, method1)
let m2 = class_getInstanceMethod(aClass, method2)
method_exchangeImplementations(m1, m2)
}
备注:
Swift类中的方法没有动态特性,即使继承了NSObject。
但在其方法前添加
dynamic
关键词后,可以获得动态特性,从而交换方法。另外:若方法的参数、属性类型为Swift特有、无法映射到Objective-C的类型(如Character、Tuple),则此方法、属性无法添加dynamic修饰(会编译错误)
属性关联
objc_setAssociatedObject
and objc_getAssociatedObject
// Objective-C
static char nameKey;
-(void)setName:(NSString *)name{
objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
-(NSString *)name{
return objc_getAssociatedObject(self, &nameKey);
}
// Swift
extension MyClass {
private static var nameKey: UInt8 = 0
var name: String?{
set{
objc_setAssociatedObject(self, &MyClass.nameKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
get{
return objc_getAssociatedObject(self, &MyClass.nameKey) as? String
}
}
}