成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

HandyJSON:Swift語(yǔ)言JSON轉(zhuǎn)Model工具庫(kù)

移動(dòng)開(kāi)發(fā) 移動(dòng)應(yīng)用
JSON是移動(dòng)端開(kāi)發(fā)常用的應(yīng)用層數(shù)據(jù)交換協(xié)議。最常見(jiàn)的場(chǎng)景便是,客戶端向服務(wù)端發(fā)起網(wǎng)絡(luò)請(qǐng)求,服務(wù)端返回JSON文本,然后客戶端解析這個(gè)JSON文本,再把對(duì)應(yīng)數(shù)據(jù)展現(xiàn)到頁(yè)面上。

背景

JSON是移動(dòng)端開(kāi)發(fā)常用的應(yīng)用層數(shù)據(jù)交換協(xié)議。最常見(jiàn)的場(chǎng)景便是,客戶端向服務(wù)端發(fā)起網(wǎng)絡(luò)請(qǐng)求,服務(wù)端返回JSON文本,然后客戶端解析這個(gè)JSON文本,再把對(duì)應(yīng)數(shù)據(jù)展現(xiàn)到頁(yè)面上。

但在編程的時(shí)候,處理JSON是一件麻煩事。在不引入任何輪子的情況下,我們通常需要先把JSON轉(zhuǎn)為Dictionary,然后還要記住每個(gè)數(shù)據(jù)對(duì)應(yīng)的Key,用這個(gè)Key在Dictionary中取出對(duì)應(yīng)的Value來(lái)使用。這個(gè)過(guò)程我們會(huì)犯各種錯(cuò)誤:

  • Key拼寫(xiě)錯(cuò)了;
  • 路徑寫(xiě)錯(cuò)了;
  • 類型搞錯(cuò)了;
  • 沒(méi)拿到值懵逼了;
  • 某一天和服務(wù)端約定的某個(gè)字段變更了,沒(méi)能更新所有用到它的地方;
  • ...

為了解決這些問(wèn)題,很多處理JSON的開(kāi)源庫(kù)應(yīng)運(yùn)而生。在Swift中,這些開(kāi)源庫(kù)主要朝著兩個(gè)方向努力:

  • 保持JSON語(yǔ)義,直接解析JSON,但通過(guò)封裝使調(diào)用方式更優(yōu)雅、更安全;
  • 預(yù)定義Model類,將JSON反序列化為類實(shí)例,再使用這些實(shí)例;

對(duì)于1,使用最廣、評(píng)價(jià)***的庫(kù)非 SwiftyJSON 莫屬,它很能代表這個(gè)方向的核心。它本質(zhì)上仍然是根據(jù)JSON結(jié)構(gòu)去取值,使用起來(lái)順手、清晰。但也正因如此,這種做法沒(méi)能妥善解決上述的幾個(gè)問(wèn)題,因?yàn)镵ey、路徑、類型仍然需要開(kāi)發(fā)者去指定;

對(duì)于2,我個(gè)人覺(jué)得這是更合理的方式。由于Model類的存在,JSON的解析和使用都受到了定義的約束,只要客戶端和服務(wù)端約定好了這個(gè)Model類,客戶端定義后,在業(yè)務(wù)中使用數(shù)據(jù)時(shí)就可以享受到語(yǔ)法檢查、屬性預(yù)覽、屬性補(bǔ)全等好處,而且一旦數(shù)據(jù)定義變更,編譯器會(huì)強(qiáng)制所有用到的地方都改過(guò)來(lái)才能編譯通過(guò),非常安全。這個(gè)方向上,開(kāi)源庫(kù)們做的工作,主要就是把JSON文本反序列化到Model類上了。這一類JSON庫(kù)有 ObjectMapper、JSONNeverDie、HandyJSON 等。而 HandyJSON 是其中使用最舒服的一個(gè)庫(kù),本文將介紹用 HandyJSON 來(lái)進(jìn)行Model和JSON間的互相轉(zhuǎn)換。

項(xiàng)目地址:https://github.com/alibaba/handyjson

為什么用HandyJSON

在Swift中把JSON反序列化到Model類,在HandyJSON出現(xiàn)以前,主要使用兩種方式:

  • 讓Model類繼承自NSObject,然后class_copyPropertyList()方法獲取屬性名作為Key,從JSON中取得Value,再通過(guò)Objective-C runtime支持的KVC機(jī)制為類屬性賦值;如JSONNeverDie;
  • 支持純Swift類,但要求開(kāi)發(fā)者實(shí)現(xiàn)Mapping函數(shù),使用重載的運(yùn)算符進(jìn)行賦值,如ObjectMapper;

這兩者都有顯而易見(jiàn)的缺點(diǎn)。前者要求Model繼承自NSObject,非常不優(yōu)雅,且直接否定了用struct來(lái)定義Model的方式;后者的Mapping函數(shù)要求開(kāi)發(fā)者自定義,在其中指明每個(gè)屬性對(duì)應(yīng)的JSON字段名,代碼侵入大,且仍然容易發(fā)生拼寫(xiě)錯(cuò)誤、維護(hù)困難等問(wèn)題。

而HandyJSON另辟蹊徑,采用Swift反射+內(nèi)存賦值的方式來(lái)構(gòu)造Model實(shí)例,規(guī)避了上述兩個(gè)方案遇到的問(wèn)題。

把JSON轉(zhuǎn)換為Model

簡(jiǎn)單類型

某個(gè)Model類想支持通過(guò)HandyJSON來(lái)反序列化,只需要在定義時(shí),實(shí)現(xiàn)HandyJSON協(xié)議,這個(gè)協(xié)議只要求實(shí)現(xiàn)一個(gè)空的init()函數(shù)。

比如我們和服務(wù)端約定了一個(gè)Animal數(shù)據(jù),里面有name/id/num字段,那么我們這樣定義Animal類:

  1. class Animal: HandyJSON { 
  2.     var name: String? 
  3.     var id: String? 
  4.     var num: Int
  5.  
  6.     required init() {} 

然后假設(shè)我們從服務(wù)端拿到這樣一個(gè)JSON文本: 

  1. let jsonString = "{\"name\":\"cat\",\"id\":\"12345\",\"num\":180}" 

引入HandyJSON以后,我們就可以這樣來(lái)做反序列化了:

  1. if let animal = JSONDeserializer<Animal>.deserializeFrom(json: jsonString) { 
  2.     print(animal.name
  3.     print(animal.id) 
  4.     print(animal.num) 

簡(jiǎn)單吧~

支持Struct

如果Model的定義是struct,由于Swift中struct提供了默認(rèn)構(gòu)造函數(shù),所以就不需要再實(shí)現(xiàn)空的init()函數(shù)了。但需要注意,如果你為strcut指定了別的構(gòu)造函數(shù),那么就需要保留一個(gè)空的實(shí)現(xiàn)。

  1. struct Animal: HandyJSON { 
  2.     var name: String? 
  3.     var id: String? 
  4.     var num: Int
  5.  
  6. let jsonString = "{\"name\":\"cat\",\"id\":\"12345\",\"num\":180}" 
  7.  
  8. if let animal = JSONDeserializer<Animal>.deserializeFrom(json: jsonString) { 
  9.     print(animal) 

比較復(fù)雜的類型

HandyJSON支持在類定義里使用各種形式的基本屬性,包括可選(?),隱式解包可選(!),數(shù)組(Array),字典(Dictionary),Objective-C基本類型(NSString、NSNumber),各種類型的嵌套([Int]?、[String]?、[Int]!、...)等等。比如下面這個(gè)看起來(lái)比較復(fù)雜的類型:

  1. class Cat: HandyJSON { 
  2.     var id: Int64! 
  3.     var name: String! 
  4.     var friend: [String]? 
  5.     var weight: Double
  6.     var alive: Bool = true 
  7.     var color: NSString? 
  8.  
  9.     required init() {} 

一樣輕松轉(zhuǎn)換:

  1. let jsonString = "{\"id\":1234567,\"name\":\"Kitty\",\"friend\":[\"Tom\",\"Jack\",\"Lily\",\"Black\"],\"weight\":15.34,\"alive\":false,\"color\":\"white\"}" 
  2.  
  3. if let cat = JSONDeserializer<Cat>.deserializeFrom(json: jsonString) { 
  4.     print(cat.xxx) 

嵌套的Model類

如果Model類中的某個(gè)屬性是另一個(gè)自定義的Model類,那么只要那個(gè)Model類也實(shí)現(xiàn)了HandyJSON協(xié)議,就一樣可以轉(zhuǎn)換:

  1. class Component: HandyJSON { 
  2.     var aInt: Int
  3.     var aString: String? 
  4.  
  5.     required init() {} 
  6.  
  7. class Composition: HandyJSON { 
  8.     var aInt: Int
  9.     var comp1: Component? 
  10.     var comp2: Component? 
  11.  
  12.     required init() {} 
  13.  
  14. let jsonString = "{\"num\":12345,\"comp1\":{\"aInt\":1,\"aString\":\"aaaaa\"},\"comp2\":{\"aInt\":2,\"aString\":\"bbbbb\"}}" 
  15.  
  16. if let composition = JSONDeserializer<Composition>.deserializeFrom(json: jsonString) { 
  17.     print(composition) 

指定反序列化JSON中某個(gè)節(jié)點(diǎn)

有時(shí)候服務(wù)端返回給我們的JSON文本包含了大量的狀態(tài)信息,和Model無(wú)關(guān),比如statusCode,debugMessage等,或者有用的數(shù)據(jù)是在某個(gè)節(jié)點(diǎn)以下,那么我們可以指定反序列化哪個(gè)節(jié)點(diǎn):

  1. class Cat: HandyJSON { 
  2.     var id: Int64! 
  3.     var name: String! 
  4.  
  5.     required init() {} 
  6.  
  7.  
  8. // 服務(wù)端返回了這個(gè)JSON,我們想解析的只有data里的cat 
  9. let jsonString = "{\"code\":200,\"msg\":\"success\",\"data\":{\"cat\":{\"id\":12345,\"name\":\"Kitty\"}}}" 
  10.  
  11. // 那么,我們指定解析 "data.cat",通過(guò)點(diǎn)來(lái)表達(dá)路徑 
  12. if let cat = JSONDeserializer<Cat>.deserializeFrom(json: jsonString, designatedPath: "data.cat") { 
  13.     print(cat.name

有繼承關(guān)系的Model類

如果某個(gè)Model類繼承自另一個(gè)Model類,只需要這個(gè)父Model類實(shí)現(xiàn)HandyJSON協(xié)議就可以:

  1. class Animal: HandyJSON { 
  2.     var id: Int
  3.     var color: String? 
  4.  
  5.     required init() {} 
  6.  
  7.  
  8. class Cat: Animal { 
  9.     var name: String? 
  10.  
  11.     required init() {} 
  12.  
  13. let jsonString = "{\"id\":12345,\"color\":\"black\",\"name\":\"cat\"}" 
  14.  
  15. if let cat = JSONDeserializer<Cat>.deserializeFrom(json: jsonString) { 
  16.     print(cat) 

自定義解析方式

HandyJSON還提供了一個(gè)擴(kuò)展能力,就是允許自行定義Model類某個(gè)字段的解析Key、解析方式。我們經(jīng)常會(huì)有這樣的需求:

  • 某個(gè)Model中,我們不想使用和服務(wù)端約定的key作為屬性名,想自己定一個(gè);
  • 有些類型如enum、tuple是無(wú)法直接從JSON中解析出來(lái)的,但我們?cè)贛odel類中有這樣的屬性;

HandyJSON協(xié)議提供了一個(gè)可選的mapping()函數(shù),我們可以在其中指定某個(gè)字段用什么Key、或者用什么方法從JSON中解析出它的值。如我們有一個(gè)Model類和一個(gè)服務(wù)端返回的JSON串:

  1. class Cat: HandyJSON { 
  2.     var id: Int64! 
  3.     var name: String! 
  4.     var parent: (String, String)? 
  5.  
  6.     required init() {} 
  7.  
  8. let jsonString = "{\"cat_id\":12345,\"name\":\"Kitty\",\"parent\":\"Tom/Lily\"}" 

可以看到,Cat類的id屬性和JSON文本中的Key是對(duì)應(yīng)不上的;而對(duì)于parent這個(gè)屬性來(lái)說(shuō),它是一個(gè)元組,做不到從JSON中的"Tom/Lily"解析出來(lái)。所以我們要定義一個(gè)Mapping函數(shù)來(lái)做這兩個(gè)支持:

  1. class Cat: HandyJSON { 
  2.     var id: Int64! 
  3.     var name: String! 
  4.     var parent: (String, String)? 
  5.  
  6.     required init() {} 
  7.  
  8.     func mapping(mapper: HelpingMapper) { 
  9.         // 指定 id 字段用 "cat_id" 去解析 
  10.         mapper.specify(property: &id, name"cat_id"
  11.  
  12.         // 指定 parent 字段用這個(gè)方法去解析 
  13.         mapper.specify(property: &parent) { (rawString) -> (String, String) in 
  14.             let parentNames = rawString.characters.split{$0 == "/"}.map(String.init) 
  15.             return (parentNames[0], parentNames[1]) 
  16.         } 
  17.     } 

就這樣,HandyJSON***地幫我們進(jìn)行了JSON到Model類的轉(zhuǎn)換。如此方便,這也是將其命名為Handy的由來(lái)。

把Model轉(zhuǎn)換為JSON文本

HandyJSON還提供了把Model類序列化為JSON文本的能力,簡(jiǎn)直無(wú)情。

基本類型

如果只需要進(jìn)行序列化,那么在定義Model類時(shí),不需要做任何特殊的改動(dòng)。任何一個(gè)類的實(shí)例,直接調(diào)用HandyJSON的序列化方法去序列化,就能得到JSON字符串了。

  1. class Animal { 
  2.     var name: String? 
  3.     var height: Int
  4.  
  5.     init(name: String, height: Int) { 
  6.         self.name = name 
  7.         self.height = height 
  8.     } 
  9.  
  10. let cat = Animal(name"cat", height: 30) 
  11.  
  12. // 序列化為簡(jiǎn)單JSON文本 
  13. if let jsonStr = JSONSerializer.serialize(model: cat).toJSON() { 
  14.     print("simple json string: ", jsonStr) 
  15.  
  16. // 序列化為格式化的JSON文本 
  17. if let prettifyJSON = JSONSerializer.serialize(model: cat).toPrettifyJSON() { 
  18.     print("prettify json string: ", prettifyJSON) 
  19.  
  20. // 序列化為簡(jiǎn)單字典 
  21. if let dict = JSONSerializer.serialize(model: cat).toSimpleDictionary() { 
  22.     print("dictionary: ", dict) 

復(fù)雜類型

即使Model類中有別的Model類啥的,都一樣支持。

  1. enum Gender { 
  2.     case Male 
  3.     case Female 
  4.  
  5. struct Subject { 
  6.     var id: Int64? 
  7.     var name: String? 
  8.  
  9.     init(id: Int64, name: String) { 
  10.         self.id = id 
  11.         self.name = name 
  12.     } 
  13.  
  14. class Student { 
  15.     var name: String? 
  16.     var gender: Gender? 
  17.     var subjects: [Subject]? 
  18.  
  19. let student = Student() 
  20. student.name = "Jack" 
  21. student.gender = .Female 
  22. student.subjects = [Subject(id: 1, name"math"), Subject(id: 2, name"English"), Subject(id: 3, name"Philosophy")] 
  23.  
  24. if let jsonStr = JSONSerializer.serialize(model: student).toJSON() { 
  25.     print("simple json string: ", jsonStr) 
  26. if let prettifyJSON = JSONSerializer.serialize(model: student).toPrettifyJSON() { 
  27.     print("prettify json string: ", prettifyJSON) 
  28. if let dict = JSONSerializer.serialize(model: student).toSimpleDictionary() { 
  29.     print("dictionary: ", dict) 

總結(jié)

有了HandyJSON的支持,現(xiàn)在我們可以開(kāi)心地在Swift中使用JSON了。這個(gè)庫(kù)支持了Swift 2.2+, Swift 3.0+。如果大家有什么需求或者建議,可以去 https://github.com/alibaba/handyjson 提issue.

為何開(kāi)發(fā)HandyJSON

我所在iOS團(tuán)隊(duì)是從去年11月份切Swift的。我們服務(wù)端和客戶端數(shù)據(jù)交互格式一直用的是JSON,而當(dāng)時(shí)Swift中處理JSON名氣比較大的庫(kù)貌似只有SwiftyJSON,工程切到Swift后,我們也用了這個(gè)庫(kù)。用上之后,需求是滿足了,但是對(duì)一些復(fù)雜的Model,代碼寫(xiě)得看起來(lái)非常糟糕,因?yàn)槊看稳≈刀夹枰? json["akey"]["bkey"]["ckey"].value 形式,寫(xiě)的時(shí)候?qū)χ臋n沒(méi)覺(jué)得啥問(wèn)題,但過(guò)后在脫離文檔的情況下,通篇都是字符串表達(dá)的key,很難從代碼中感覺(jué)出Model結(jié)構(gòu)。所以我們都會(huì)把一段sample數(shù)據(jù)寫(xiě)在注釋里。但仍然比較凌亂,另外key寫(xiě)錯(cuò)了debug起來(lái)也費(fèi)勁,一個(gè)大小寫(xiě)問(wèn)題有時(shí)候debug半天。

于是我們進(jìn)化了一下,先寫(xiě)好Model,然后Model類中寫(xiě)convert函數(shù),也用上了KVC遍歷key賦值。寫(xiě)起來(lái)舒服多了,但還是麻煩,而且要求每一個(gè)類都繼承自NSObject。不久后,我們認(rèn)識(shí)了ObjectMapper庫(kù),二話不說(shuō),就換了上去。世界頓時(shí)干凈多了。

但還是感覺(jué)差了一點(diǎn),因?yàn)镺bjectMapper需要自己指明映射關(guān)系。通常JSON中key和Model中字段名都是一致的,每次都要額外寫(xiě)一坨東西,總覺(jué)得多余,字段有改動(dòng)的時(shí)候也費(fèi)勁。新來(lái)剛接觸Swift的同事,也表示不太舒服,因?yàn)樗麄冎笆褂玫腏SON反序列化庫(kù),無(wú)論Java中還是Objective-C中,都是自然使用Model字段名去取值的。

所以就想著研究一下,Swift中能不能做到這種效果。

HandyJSON的設(shè)計(jì)思路

Swift中存在的限制

無(wú)論是Java或者Objective-C中的JSON反序列化庫(kù),通常都是,在運(yùn)行時(shí)獲取Model的字段名集合,遍歷該集合,拿Key去JSON中取值并完成賦值。這些步驟,Java依賴反射機(jī)制可以實(shí)現(xiàn),Objective-C通過(guò)class_copyPropertyList方法加上KVC機(jī)制,也能輕松實(shí)現(xiàn)。而Swift會(huì)卡在***一步:無(wú)法賦值。

Swift的反射是只讀的,就是說(shuō),我們能在運(yùn)行時(shí)獲取一個(gè)Model實(shí)例的所有字段、字段值,但卻無(wú)法給它賦值。事實(shí)上,我們拿到的value是原值的一個(gè)只讀拷貝,即使獲取到這個(gè)拷貝的地址寫(xiě)入新值,也是無(wú)效的。

  1. class Animal { 
  2.     var name: String? 
  3.  
  4. Mirror(reflecting: Animal()).children.forEach { (child) in 
  5.     print(child.label ?? "", child.value) // working correctly 
  6.     child.value = "cat" // error,不能直接賦值 

而且迄今,蘋(píng)果官網(wǎng)文檔上對(duì)實(shí)現(xiàn)反射機(jī)制的Mirror類仍然是這么描述: Mirrors are used by playgrounds and the debugger,態(tài)度非常含糊,似乎不太鼓勵(lì),但生產(chǎn)中很多類庫(kù)都用上了。只能說(shuō),蘋(píng)果不會(huì)輕易撤下這個(gè)能力,但期待它對(duì)這個(gè)能力做出改進(jìn)(比如支持運(yùn)行時(shí)賦值),是希望渺茫的。

如何繞過(guò)限制

最簡(jiǎn)單的方式,就是在Swift中定義Model時(shí)繼承NSObject,讓這個(gè)Model的實(shí)例存在于objc運(yùn)行時(shí)中,上述的class_copyPropertyList方法和KVC就能用上了。目前看見(jiàn)的Swift中不需要指明映射關(guān)系的JSON庫(kù),都是這種方式。

然后就是以O(shè)bjectMapper為代表的庫(kù),通過(guò)運(yùn)算符重載,在指定映射關(guān)系時(shí)完成賦值。走這一類實(shí)現(xiàn)的庫(kù)也非常多了。

但我想做到的是,既支持運(yùn)行在Swift運(yùn)行時(shí)的純Swift類,又不需要顯示指定每一個(gè)字段的映射關(guān)系。那么,不能走反射賦值,那就直接寫(xiě)入內(nèi)存吧。

具體實(shí)現(xiàn)

Swift中,一個(gè)類實(shí)例的內(nèi)存布局是有規(guī)律的:

  • 32位機(jī)器上,類前面有4+8個(gè)字節(jié)存儲(chǔ)meta信息,64位機(jī)器上,有8+8個(gè)字節(jié);
  • 內(nèi)存中,字段從前往后有序排列;
  • 如果該類繼承自某一個(gè)類,那么父類的字段在前;
  • Optional會(huì)增加一個(gè)字節(jié)來(lái)存儲(chǔ).None/.Some信息;
  • 每個(gè)字段需要考慮內(nèi)存對(duì)齊;

這方面基本沒(méi)有官方的資料參考,上述規(guī)律一些是從網(wǎng)上其他大神的總結(jié)中收集,一些從Clang的一些說(shuō)明文檔中挖掘,還有一些是自己在playground里試出來(lái)的。開(kāi)始心里沒(méi)什么底,但把HandyJSON實(shí)現(xiàn)出來(lái)使用這么久了,還沒(méi)出過(guò)狀況,可以認(rèn)為是靠譜的。

有法子計(jì)算內(nèi)存布局,剩下的事情就比較簡(jiǎn)單了。對(duì)一個(gè)實(shí)例:

  • 獲取它的起始指針,移動(dòng)到有效起點(diǎn);
  • 通過(guò)Mirror獲取每一個(gè)字段的字段名和字段類型;
  • 根據(jù)字段名在JSON中取值,轉(zhuǎn)換為和字段一樣的類型,通過(guò)指針寫(xiě)入;
  • 根據(jù)本字段類型的占位大小和下一個(gè)字段類型計(jì)算下一個(gè)字段的對(duì)齊起點(diǎn);
  • 移動(dòng)指針,繼續(xù)處理;

獲取類實(shí)例的起始指針

Swift中,獲取struct實(shí)例起始指針和獲取class實(shí)例起始指針的方法是不一樣的,和語(yǔ)言版本也相關(guān)。在Swift3中:

  1. // 獲取struct實(shí)例起始指針 
  2. mutating func headPointerOfStruct() -> UnsafeMutablePointer<Byte> { 
  3.  
  4.     return withUnsafeMutablePointer(to: &self) { 
  5.         return UnsafeMutableRawPointer($0).bindMemory(to: Byte.self, capacity: MemoryLayout<Self>.stride) 
  6.     } 
  7.  
  8. // 獲取class實(shí)例起始指針 
  9. mutating func headPointerOfClass() -> UnsafeMutablePointer<Byte> { 
  10.  
  11.     let opaquePointer = Unmanaged.passUnretained(self as AnyObject).toOpaque() 
  12.     let mutableTypedPointer = opaquePointer.bindMemory(to: Byte.self, capacity: MemoryLayout<Self>.stride) 
  13.     return UnsafeMutablePointer<Byte>(mutableTypedPointer) 

通過(guò)Mirror獲取字段名、類型

  1. Mirror(reflecting: Animal()).children.forEach { (child) in 
  2.     print(child.label ?? "") // 獲取字段名 
  3.     print(type(of: child.value)) // 獲取字段類型 

計(jì)算Model的每個(gè)屬性字段占位大小

Swift3暴露了兩個(gè)接口用于計(jì)算類型占位大小:MemoryLayout.size(ofValue: T)和MemoryLayout.size。這兩者都沒(méi)辦法直接用,因?yàn)椋?/p>

  • 對(duì)于每個(gè)屬性,我們目前只持有它的起始指針,而不是它的實(shí)例,***個(gè)接口用不上;
  • 對(duì)于每個(gè)屬性,我們是在運(yùn)行時(shí)中獲取到它的類型,已經(jīng)沒(méi)辦法再實(shí)例化出泛型類型MemoryLayout<T>來(lái)計(jì)算size。所以,我引入了HandyJSON類,在擴(kuò)展中實(shí)現(xiàn)函數(shù):
  1. protocol HandyJSON { 
  2.  
  3. extension HandyJSON { 
  4.     static func size() -> Int { 
  5.         return MemoryLayout<Self>.size 
  6.     } 

于是,對(duì)于每一個(gè)實(shí)現(xiàn)HandyJSON協(xié)議的Model類T,直接調(diào)用 T.size() 就能獲取到T的size了。

內(nèi)存對(duì)齊的影響

類實(shí)例的屬性并不是直接按照各自占位大小依次往下排列的,不然事情就簡(jiǎn)單了。和C/C++一樣,Swift中實(shí)例內(nèi)存布局也考慮了內(nèi)存對(duì)齊。翻閱了Swift的docs和LLVM的一些資料,MemoryLayout提供了一個(gè)接口:MemoryLayout.alignment,對(duì)齊的規(guī)則為,每個(gè)字段的起始地址必須為alignment值的整數(shù)倍。細(xì)節(jié)的出處我已經(jīng)忘記。當(dāng)時(shí)進(jìn)行了一些復(fù)雜類型的測(cè)試后,認(rèn)定它符合事實(shí)。所以HandyJSON中計(jì)算下一個(gè)字段起始地址的函數(shù)為:

  1. // Returns the offset to the next integer that is greater than 
  2. // or equal to Value and is a multiple of Align. Align must be 
  3. // non-zero. 
  4. static func offsetToAlignment(value: Int, align: Int) -> Int { 
  5.     let m = value % align 
  6.     return m == 0 ? 0 : (align - m) 

其他情況

基本類型按照上述方法處理就可以了,還有可選類型、數(shù)組類型、字典類型,通過(guò)遍歷、遞歸解析等方式,處理方法也類似。如數(shù)組:

  1. extension Array: ArrayTypeProtocol { 
  2.     static func getWrappedType() -> Any.Type { 
  3.         return Element.self 
  4.     } 
  5.  
  6.     static func castArrayType(arr: [Any]) -> Array<Element> { 
  7.         return arr.map({ (p) -> Element in 
  8.             return p as! Element 
  9.         }) 
  10.     } 

獲取到Array泛型實(shí)參類型,然后構(gòu)造出該類型的一個(gè)數(shù)組,完成賦值就可以了。

結(jié)語(yǔ)

主要流程就是這樣了,也比較簡(jiǎn)單,剩下處理繼承、組合等情況,只是實(shí)現(xiàn)問(wèn)題,就不再贅述了。總覺(jué)得自己對(duì)Swift指針這一套設(shè)施理解還不是很到位,也許有更好的用法,比如說(shuō),完全不需要空的init()函數(shù)就可以初始化出一個(gè)類的實(shí)例。有同學(xué)在這方面有更深入理解,有什么意見(jiàn)或者建議的,歡迎交流~

參考

  • https://appventure.me/2015/10/24/swift-reflection-api-what-you-can-do/
  • https://www.raywenderlich.com/119881/enums-structs-and-classes-in-swift
  • http://vizlabxt.github.io/blog/2014/12/23/Swift-Memory/
  • https://realm.io/news/russ-bishop-unsafe-swift/
  • http://sketchytech.blogspot.jp/2014/10/becoming-less-afraid-of-unsafe-mutable.html
  • http://southpeak.github.io/blog/2014/07/06/ios-swift-cpointer-2/
  • https://onevcat.com/2015/01/swift-pointer/
  • https://appventure.me/2015/10/17/advanced-practical-enum-examples/
  • https://github.com/Hearst-DD/ObjectMapper
責(zé)任編輯:龐桂玉 來(lái)源: 移動(dòng)開(kāi)發(fā)前線
相關(guān)推薦

2016-04-11 09:58:53

iOSJSONModel

2015-10-28 09:55:39

Swift解析生產(chǎn)庫(kù)

2015-08-14 11:37:37

Swift語(yǔ)言中文版

2016-06-07 14:42:18

Swift設(shè)計(jì)

2014-08-20 10:40:29

Xcode 6

2017-02-27 16:28:00

2014-07-22 09:01:53

SwiftJSON

2022-02-25 09:03:49

工具Swift項(xiàng)目

2015-04-17 16:07:11

swiftOC

2010-06-30 11:16:50

SQL Server

2014-06-04 10:52:56

Swift蘋(píng)果iOS

2015-12-07 09:46:26

swift編程開(kāi)源

2015-01-12 13:04:39

Swift開(kāi)源項(xiàng)目匯總

2014-06-03 10:44:20

Swift開(kāi)發(fā)語(yǔ)言

2023-08-28 08:40:23

Sonic開(kāi)發(fā)JSON

2022-10-13 21:07:48

數(shù)據(jù)庫(kù)SQL Server

2021-04-21 00:10:12

對(duì)象JSON插件

2015-04-14 14:23:38

蘋(píng)果Swift編程語(yǔ)言

2014-07-16 09:41:12

Swift傳統(tǒng)編程

2014-07-29 10:49:23

Swift
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 亚洲精品久久久久久一区二区 | 欧美一级特黄aaa大片在线观看 | 99热精品在线观看 | 精品国产一区久久 | 欧美a在线看 | 九色国产 | 亚洲黄色av网站 | 嫩草视频入口 | 懂色中文一区二区在线播放 | 国产精品精品视频 | 亚洲欧美一区二区三区国产精品 | 日本羞羞影院 | 9191av| 一级片网站视频 | 国产999精品久久久久久绿帽 | 久久久久国产一区二区三区四区 | 中文字幕第一页在线 | 亚洲精品一区二区网址 | 久热精品在线 | 成人高清视频在线观看 | 国产精品久久久久久av公交车 | 91精品在线观看入口 | 精品国产91| 亚洲www啪成人一区二区 | 国产精品一区二区在线播放 | 国产综合精品一区二区三区 | 久久久久亚洲视频 | 在线午夜 | 欧美日韩在线视频观看 | 久久精品国产亚洲一区二区三区 | 国产精品麻 | 成人欧美一区二区三区视频xxx | 午夜爽爽男女免费观看hd | 成人三级在线播放 | 午夜性色a√在线视频观看9 | 国产高清一区二区三区 | 国产婷婷精品av在线 | 澳门永久av免费网站 | 欧美最猛黑人 | 欧美日韩久久 | 日韩在线欧美 |