ios開發之Swift - 反射(Reflection)的介紹與使用範例(附KVC介紹)

來源:互聯網
上載者:User

1,反射(Reflection)
對於C#、Java開發人員來說,肯定都對反射這個概念相當熟悉。所謂反射就是可以動態擷取類型、成員資訊,同時在運行時(而非編譯時間)可以動態調用任意方法、屬性等行為的特性。
以Java上的兩個知名架構(hibernate和spring)為例。hibernate的屬性對應就是通過反射來賦值的,spring的bean的建立就是根據配置的class來反射構建的。

2,Objective-C 的 Runtime
在使用ObjC開發時很少強調其反射概念,因為ObjC的Runtime要比其他語言中的反射強大的多。在ObjC中可以很簡單的實現字串和類型的轉換(NSClassFromString()),實現動態方法引動過程(performSelector: withObject:),動態賦值(KVC)等等。

3,Swift中的反射
在Swift中並不提倡使用Runtime,而是像其他語言一樣使用反射(Reflect)。當然,目前Swift中的反射還沒有其他語言中的反射功能強大,不僅遠不及OC的Runtime,離Java的反射也有一定的距離。
Swift的反射機制是基於一個叫 Mirror 的 struct 來實現的,其內部有如下屬性和方法:


let children: Children   //對象的子節點。
displayStyle: Mirror.DisplayStyle?   //對象的展示風格
let subjectType: Any.Type   //對象的類型
func superclassMirror() -> Mirror?   //對象父類的 mirror

4,Swift反射的使用範例

範例1:輸出實體物件的類名,屬性個數,以及所有屬性的屬性名稱和屬性值。
首先定義一個使用者類:


//使用者類
class User {
    var name:String = ""  //姓名
    var nickname:String?  //暱稱
    var age:Int?   //年齡
    var emails:[String]?  //郵件地址
}

接著建立一個使用者物件,並通過反射擷取這個對象的資訊:


//建立一個User執行個體對象
let user1 = User()
user1.name = "hangge"
user1.age = 100
user1.emails = ["hangge@hangge.com","system@hangge.com"]
        
//將user對象進行反射
let hMirror = Mirror(reflecting: user1)
        
print("物件類型:\(hMirror.subjectType)")
print("對象子項目個數:\(hMirror.children.count)")
        
print("--- 對象子項目的屬性名稱和屬性值分別如下 ---")
for case let (label?, value) in hMirror.children {
    print("屬性:\(label)     值:\(value)")
}

控制台輸出資訊如下: 

 

範例2:通過屬性名稱(字串)擷取對應的屬性值,並對值做類型判斷(包括是否為空白)
首先為方便使用,這裡定義兩個方法。getValueByKey()是用來根據傳入的屬性名稱字串來擷取對象中對應的屬性值。unwrap()是用來給可選類型拆包的(對於非可選類型則返回原值)


//根據屬性名稱字串擷取屬性值
func getValueByKey(obj:AnyObject, key: String) -> Any {
    let hMirror = Mirror(reflecting: obj)
    for case let (label?, value) in hMirror.children {
        if label == key {
            return unwrap(value)
        }
    }
    return NSNull()
}
 
//將可選類型(Optional)拆包
func unwrap(any:Any) -> Any {
    let mi = Mirror(reflecting: any)
    if mi.displayStyle != .Optional {
        return any
    }
    
    if mi.children.count == 0 { return any }
    let (_, some) = mi.children.first!
    return some
}

下面是實際測試範例,同樣用上例的User對象做測試:


//建立一個User執行個體對象
let user1 = User()
user1.name = "hangge"
user1.age = 100
user1.emails = ["hangge@hangge.com","system@hangge.com"]
 
//通過屬性名稱字串擷取對應的值
let name = getValueByKey(user1, key: "name")
let nickname = getValueByKey(user1, key: "nickname")
let age = getValueByKey(user1, key: "age")
let emails = getValueByKey(user1, key: "emails")
let tel = getValueByKey(user1, key: "tel")
print(name, nickname, age, emails, tel)
 
//當然對於擷取到的值也可以進行類型判斷
if name is NSNull {
    print("name這個屬性不存在")
}else if (name as? AnyObject) == nil {
    print("name這個屬性是個可選類型,且為nil")
}else if name is String {
    print("name這個屬性String類型,其值為:\(name)")
}
 
if nickname is NSNull {
    print("nickname這個屬性不存在")
}else if (nickname as? AnyObject) == nil {
    print("nickname這個屬性是個可選類型,且為nil")
}else if nickname is String {
    print("nickname這個屬性String類型,其值為:\(nickname)")
}
 
if tel is NSNull {
    print("tel這個屬性不存在")
}else if (tel as? AnyObject) == nil {
    print("tel這個屬性是個可選類型,且為nil")
}else if tel is String {
    print("tel這個屬性String類型,其值為:\(tel)")
}

控制台輸出資訊如下:

附:通過KVC訪問屬性值
KVC是key-value coding的縮寫。它是一種間接訪問對象的機制。其本質是依據OC中Runtime的強大動態能力來實現的。在Swift中,只要類繼承NSObject即可使用KVC。(有一個叫KVO的,它又是基於KVC,大家有興趣的可以自行研究下。)
KVC中:key的值就是屬性名稱的字串,返回的value是任意類型,需要自己轉化為需要的類型。
(注意:正由於KVC是基於Objective-C的,所以其不支援可選類型(optional)的屬性,比如上例的 var age:Int?
因此使用者類做如下改造:)


//使用者類
class User: NSObject{
    var name:String = ""  //姓名
    var nickname:String?  //暱稱
    var age:Int = 0  //年齡
    var emails:[String]?  //郵件地址
}

KVC主要就是兩個方法:

(1)通過key獲得對應的屬性值


//建立一個User執行個體對象
let user1 = User()
user1.name = "hangge"
user1.age = 100
user1.emails = ["hangge@hangge.com","system@hangge.com"]
 
//使用KVC取值
let name = user1.valueForKey("name")
let nickname = user1.valueForKey("nickname")
let age = user1.valueForKey("age")
let emails = user1.valueForKey("emails")
//let tel = user1.valueForKey("tel")
print(name, nickname, age, emails)
 
        
//當然對於擷取到的值也可以進行類型判斷
if name == nil {
    print("name這個屬性是個可選類型,且為nil")
}else if name is String {
    print("name這個屬性String類型,其值為:\(name)")
}
      
if nickname == nil {
    print("nickname這個屬性是個可選類型,且為nil")
}else if nickname is String {
    print("nickname這個屬性String類型,其值為:\(nickname)")
}


(2)通過key設定對應的屬性值


//建立一個User執行個體對象
let user1 = User()
 
//使用KVC賦值
user1.setValue("hangge", forKey: "name")
user1.setValue(100, forKey: "age")
user1.setValue(["hangge@hangge.com","system@hangge.com"], forKey: "emails")
        
print(user1.name, user1.nickname, user1.age, user1.emails)

原文出自:www.hangge.com  轉載請保留原文連結:http://www.hangge.com/blog/cache/detail_976.html

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.