iOS runtime--获取类信息

2019-08-23 17:57:39 浏览数 (1)

iOS runtime--获取类信息

在iOS中可以通过runtime获取一个类的相关信息:有哪些方法、有哪些协议、有哪些属性、有哪些成员变量。安排的明明白白,老铁O(∩_∩)O哈哈~

代码语言:javascript复制
class_copyMethodList        //方法列表
class_copyProtocolList      //协议列表
class_copyPropertyList      //属性列表
class_copyIvarList          //成员变量列表

准备

定义一个Person类继承Human类,一个协议包括两个方法,一个是必须实现的require,一个为可选实现的optional。

代码语言:javascript复制
@objc protocol FoodProtocol {
    func eat()
    @objc optional func drink()
}

class Human: NSObject {
    @objc var country: String = ""
}

class Person: Human, FoodProtocol {
    @objc private var name: String = ""
    @objc private var gender: String = ""
    private var age: Int = 26
    
    func eat() {
        
    }
    
    override var description: String {
        return String(format: "%@--%@--%@-%d", name, gender, country, age)
    }
}

PS:本次demo是在Playground swift实现的,所以有些操作是要加上@objc

在swift中通过NSClassFromString方法获取class时需要这个类所在文件名.类名这样拼接。在Playground中使用以下version充当文件名。

代码语言:javascript复制
class Playground : NSObject {
    static var bundleVersion: String {
        let s = self.description().replacingOccurrences(of: ".Playground", with: "")
        return s
    }
}
let version = Playground.bundleVersion

class_copyMethodList

根据NSClassFromString获取class,然后获取这个类里面有哪些方法。

代码语言:javascript复制
let className = "(version).Person"
var cls = NSClassFromString(className)

var methodCount: UInt32 = 0
let methodList = class_copyMethodList(cls, &methodCount)
for i in 0..<methodCount {
    let methodSelector = method_getName(methodList![Int(i)])
    debugPrint(methodSelector.description)
}
=============method list====================
".cxx_destruct"
"description"
"name"
"setName:"
"init"
"gender"
"setGender:"
"eat"

class_copyProtocolList

获取Person类遵从了哪些协议,并且查看协议中有哪些方法。(这里我只查看了require 实例方法)

代码语言:javascript复制
let className = "(version).Person"
var cls = NSClassFromString(className)

var protocolCount: UInt32 = 0
let protocolList = class_copyProtocolList(cls, &protocolCount)
for i in 0..<protocolCount {
    let protocolName = protocol_getName(protocolList![Int(i)])
    let protocolKey = String(cString: protocolName)
    debugPrint(protocolKey)
    
    var protocolMethodCount: UInt32 = 0
    //第一个bool类型:是否是require方法 第二个bool类型:是否是实例方法
    let protocolMethodList = protocol_copyMethodDescriptionList(protocolList![Int(i)], true, true, &protocolMethodCount)
    for j in 0..<protocolMethodCount {
        let method = protocolMethodList![Int(j)]
        debugPrint("protocol method: (method.name)")
    }
}
=============protocol list====================
"__lldb_expr_34.FoodProtocol"
"protocol method: Optional(eat)"

class_copyPropertyList

这里注意:打印所有属性中是没有age的。这是因为没有加上@objc字段。

代码语言:javascript复制
let className = "(version).Person"
var cls = NSClassFromString(className)

var propertyCount: UInt32 = 0
let propertyList = class_copyPropertyList(cls, &propertyCount)
for i in 0..<propertyCount {
    let propertyName = property_getName(propertyList![Int(i)])
    let key = String(cString: propertyName)
    debugPrint(key)
}
=============property list====================
"name"
"gender"
"description"

class_copyIvarList

这里我们打印这个类的所有成员变量,并且也打印出继承的成员变量。使用一个while语句直到NSObject类为止。

发现没有即使是没有给age字段添加@objc,通过成员变量列表也是能获取到的

代码语言:javascript复制
let p = Person()
let className = "(version).Person"
var cls = NSClassFromString(className)

while true {
    var ivarCount: UInt32 = 0
    let ivarList = class_copyIvarList(cls, &ivarCount)//父类的属性不能获取
    
    for i in 0..<ivarCount {
        let ivarName = ivar_getName(ivarList![Int(i)])
        let key = String(cString: ivarName!)
        let value = personDic[key]
        debugPrint(key)
    }
    if let superCls = cls?.superclass(), superCls.self != NSObject.self {
       cls = superCls
    } else {
        break
    }
}
=============ivar list====================
"name"
"gender"
"age"
"country"

我们给age添加上@objc,使用setValue:forKey方法直接赋值,最后查看是否赋值成功p.description。这可以用在解析model或者其他地方。

代码语言:javascript复制
let personDic = ["name": "joealzhou", "gender": "men", "country": "China", "age": 24] as [String : Any]
let p = Person()
let className = "(version).Person"
var cls = NSClassFromString(className)
while true {
    var ivarCount: UInt32 = 0
    let ivarList = class_copyIvarList(cls, &ivarCount)//父类的属性不能获取
    
    for i in 0..<ivarCount {
        let ivarName = ivar_getName(ivarList![Int(i)])
        let key = String(cString: ivarName!)
        let value = personDic[key]
        debugPrint(key, value)
        p.setValue(value, forKey: key)
    }
    if let superCls = cls?.superclass(), superCls.self != NSObject.self {
       cls = superCls
    } else {
        break
    }
}
debugPrint(p.description)
=============ivar list====================
"name" Optional("joealzhou")
"gender" Optional("men")
"age" Optional(24)
"country" Optional("China")
"joealzhou--men--China-24"

0 人点赞