0%

Swift、OC反射

Swift、OC反射

反射即根据类名字符串,生成对应实例对象的过程.

分为两步:

  1. 根据字符串,得到对应的类对象.
  2. 通过类对象调用初始化方法生成一个实例.

获取类对象

OC:

1
2
+ (Class)class OBJC_SWIFT_UNAVAILABLE("use 'aClass.self' instead");
- (Class)class OBJC_SWIFT_UNAVAILABLE("use 'type(of: anObject)' instead");

这俩方法都是返回一个类对象.

Swift:

1
2
aClass.self
type(of: anObject)

举个例子,打印类对象:

OC类在OC类里打印:

1
2
3
4
5
XQMessageTableViewCell *ocCell = [[XQMessageTableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"yyy"];
NSLog(@"+class方法:%@ -class方法:%@ classString:%@ obj:%@", XQMessageTableViewCell.class, ocCell.class, NSStringFromClass(XQMessageTableViewCell.class), ocCell);

...
2019-09-12 16:23:18.539301+0800 OSHybridDemo[33893:5942155] +class方法:XQMessageTableViewCell -class方法:XQMessageTableViewCell classString:XQMessageTableViewCell obj:<XQMessageTableViewCell: 0x7fa743821a00; baseClass = UITableViewCell; frame = (0 0; 320 44); layer = <CALayer: 0x600002940d40>>

Swift类在OC类里打印:

1
2
3
4
5
XQTableViewCell *swtCell = [[XQTableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"xxx"];
NSLog(@"+class方法:%@ -class方法:%@ classString:%@ obj:%@", XQTableViewCell.class, swtCell.class, NSStringFromClass(XQTableViewCell.class), swtCell);

...
2019-09-12 16:24:32.637695+0800 OSHybridDemo[33981:5943715] +class方法:OSHybridDemo.XQTableViewCell -class方法:OSHybridDemo.XQTableViewCell classString:OSHybridDemo.XQTableViewCell obj:<OSHybridDemo.XQTableViewCell: 0x7ffab705e200; baseClass = UITableViewCell; frame = (0 0; 320 44); layer = <CALayer: 0x60000251a6e0>>

Swift类在Swift类里打印:

1
2
3
4
5
let people = People.init()
print(People.self, type(of: people), NSStringFromClass(People.self), String.init(describing: People.self), people) //NSStringFromClass(People.self)有命名空间,而String.init(describing: People.self)没有命名空间
...

People People OSHybridDemo.People People <OSHybridDemo.People: 0x60000157ec10>

可以看到使用NSStringFromClass(People.self)有命名空间,而String.init(describing: People.self)没有命名空间.

OC类在Swift类里打印:

1
2
3
4
5
6
let ocObj = XQMessageTableViewCell.init()
print(XQMessageTableViewCell.self, type(of: ocObj), NSStringFromClass(XQMessageTableViewCell.self), String.init(describing: XQMessageTableViewCell.self), ocObj)

...

XQMessageTableViewCell XQMessageTableViewCell XQMessageTableViewCell XQMessageTableViewCell <XQMessageTableViewCell: 0x7fcad001b600; baseClass = UITableViewCell; frame = (0 0; 320 44); layer = <CALayer: 0x600000f517c0>>

总结:

使用NSStringFromClass()获取类名时,对于Swift类系统会在类名前添加命名空间.而OC没有这一概念所以没有命名空间.

反射

系统提供的方法如下:

OC:

1
2
FOUNDATION_EXPORT NSString *NSStringFromClass(Class aClass);
FOUNDATION_EXPORT Class _Nullable NSClassFromString(NSString *aClassName);

Swift:

1
2
public func NSStringFromClass(_ aClass: AnyClass) -> String
public func NSClassFromString(_ aClassName: String) -> AnyClass?

已知一个OC类名,如果需要通过该类名字符串来创建一个实例,无需指定命名空间.和之前写法一样.

1
2
3
4
let ocCls: AnyClass? = NSClassFromString("XQMessageTableViewCell")
let ocConcreteCls: XQMessageTableViewCell.Type = ocCls as! XQMessageTableViewCell.Type
let aocObj = ocConcreteCls.init(style: .default, reuseIdentifier: "reuseIdentifier")
print(aocObj)

已知一个Swift类名,通过该类名字符串来创建一个实例:

1
2
3
4
let namespace = Bundle.main.infoDictionary!["CFBundleExecutable"] as! String
let s2: People.Type = NSClassFromString(namespace + "." + "People") as! People.Type
let s2obj = s2.init()
print(s2obj)

对于Swift的类,如果需要通过该类名字符串来创建一个实例,则必须指定命名空间.

错误写法:没有拼接命名空间.

1
2
3
4
let swiftCls: AnyClass? = NSClassFromString("People") //这里其实是nil.
let swiftConcreteCls: People.Type = swiftCls as! People.Type
let swiftobj = swiftConcreteCls.init()
print(swiftobj)

封装

对于混编工程,为了使用方便,可以使用下面的封装方法.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class ZAEClassUtil: NSObject {
@objc static func getClassType(namespace: String?, className: String) -> AnyClass? {
var rs: AnyClass? = NSClassFromString(className)
if rs == nil {
let ns = namespace ?? Bundle.main.infoDictionary!["CFBundleExecutable"] as! String
rs = NSClassFromString(ns + "." + className) //如果类名对应的是Swift类则必须要拼接命名空间,如果是OC的则不需要.
}
return rs
}

@objc static func getClassString(cls: AnyClass) -> String {
let name = NSStringFromClass(cls)
if (name.contains(".")) {
return name.components(separatedBy: ".")[1];
} else {
return name;
}
}
}

使用如下:

1
2
3
4
5
6
7
let s3: XQRedView.Type = ZAEClassUtil.getClassType(namespace: nil, className: "XQRedView") as! XQRedView.Type
let s3obj = s3.init(frame: .zero)
print(s3obj)

let s4: XQMessageTableViewCell.Type = ZAEClassUtil.getClassType(namespace: nil, className: "XQMessageTableViewCell") as! XQMessageTableViewCell.Type
let s4obj = s4.init(style: .default, reuseIdentifier: "reuseIdentifier")
print(s4obj, ZAEClassUtil.getClassString(cls: s4))
觉得文章有帮助可以打赏一下哦!