4.0 kvc用法 swift_swift kvc 设置 frame.height - CSDN
  • ios-SwiftKVC出现的问题

    千次阅读 2017-10-12 19:29:37
    在这里一定要记录下,因为在Swift使用KVC,总是无法的找到Swift中创建的继承自NSObject类中的属性,每次执行的都是 override func setValue(_ value: Any?, forUndefinedKey key: String) { }原因是因为在...

    在这里一定要记录下,因为在Swift中使用KVC,总是无法的找到Swift中创建的继承自NSObject类中的属性,每次执行的都是

    override func setValue(_ value: Any?, forUndefinedKey key: String) {
            
        }
    原因是因为在Swift 4中继承 NSObject 的 swift class 不再默认全部 bridge 到 OC。也就是说如果我们想要使用KVC的话我们就需要加上@objcMembers 这么一个关键字。

    引用: 在 swift 3 中除了手动添加 @objc 声明函数支持 OC 调用还有另外一种方式:继承 NSObject。class 继承了 NSObject 后,编译器就会默认给这个类中的所有函数都标记为 @objc ,支持 OC 调用。苹果在Swift 4 中苹果修改了自动添加 @objc 的逻辑:一个继承 NSObject 的 swift 类不再默认给所有函数添加 @objc。只在实现 OC 接口和重写 OC 方法时才自动给函数添加 @objc 标识。

    所以我们在调用KVC的时候才不能被赋值。因为KVC是OC的方法。

    展开全文
  • SwiftKVC使用

    2019-01-23 11:29:22
    Swift使用KVC分为3中情况: 1、在swift3.0之前,类必须要继承自NSObject,因为KVC是OC的东西 2、在Swift4.0之后,类必须要继承自NSObject,同时还需要在属性前面加上@objc 在Swift4.0之前,编译器会默认帮我们在对象...

    在Swift中使用KVC分为3中情况:
    1、在swift3.0之前,类必须要继承自NSObject,因为KVC是OC的东西
    2、在Swift4.0之后,类必须要继承自NSObject,同时还需要在属性前面加上@objc
    在Swift4.0之前,编译器会默认帮我们在对象属性前面加上@objc
    在Swift里,有一种间接访问类属性的方法,叫做#keyPath
    class Person : NSObject {
    @objc var name:String = “”
    init(dict:[String:Any]){
    super.init()
    setValuesForKeys(dict)
    }
    }
    let p1 = Person(dict: [“name”:“lichangan”])
    let name = p1.value(forKeyPath: #keyPath(Person.name))
    print(name) //lichangan
    p1.setValue(“shuaige”, forKeyPath: #keyPath(Person.name))
    print(p1.name) //shuaige

            这就是Cocoa中的KVC机制,在Objective-C中它可以很好的工作,但移植到Swift之后,它的不足就显现出来了:
            value(forKeyPath:)方法返回的类型是Any?,这样我们就失去了类型信息,错误的赋值会直接导致运行时错误;
            只有NSObject的派生类才支持这种访问机制
     
        (3)Swift 4中设计了更智能的KeyPath
     
         class Person {
             var name:String = ""
             init(name:String){
                self.name = name;
             }
         }
     
         let p2 = Person(name:"lichangan")
         let nameKeyPath = \Person.name
         let name = p2[keyPath:nameKeyPath]
         print(name) //lichangan
         p2[keyPath:nameKeyPath] = "shuaige"
         print(p2.name) //shuaigei
     
        \Person.name 就是Swift4中新的key path用法,他是一个独立的类型,带有类型的信息。
        因此,编译器会发现错误类型的赋值,因此不会把这个错误延迟到运行时
     
        除了类型安全之外,新的KeyPath不需要继承自NSObject,也不需要使用@obj修饰属性,同时struct也可以使用新的KeyPath
    

    不同之处

    在 Swift 中处理 KVC和 Objective-C 中还是有些细微的差别。比如,Objective-C 中所有的类都继承自 NSObject,而 Swift 中却不是,所以我们在 Swift 中需要显式的声明继承自 NSObject。

    可为什么要继承自 NSObject 呢?我们在苹果官方的 KVC 文档中找到了答案。其实 KVC 机制是由一个协议 NSKeyValueCoding 定义的。NSObject 帮我们实现了这个协议,所以 KVC 核心的逻辑都在 NSObject 中,我们继承 NSObject 才能让我们的类获得 KVC 的能力。(理论上说,如果你遵循 NSKeyValueCoding 协议的接口,其实也可以自己实现 KVC 的细节,完全行得通。但在实践上,这么做就不太值得了,太费时间了~)。

    另外,因为 Swift 中的 Optional 机制,所以 valueForKey 方法返回的是一个 Optional 值,我们还需要对返回值做一次解包处理,才能得到实际的属性值。

    展开全文
  • 参考swift4.0字典转模型:https://www.cnblogs.com/shaoting/p/8087153.html     =====================kvc字典转模型========================= *****第一层模型 import UIKit class ...

    参考swift4.0字典转模型:https://www.cnblogs.com/shaoting/p/8087153.html

     

     

    =====================kvc字典转模型=========================

    
    
    *****第一层模型
    
    import UIKit
    
    
    
    class LYBHomeLunboModel: NSObject {
    
        @objc var name:String?="11"
    
         
    
        @objc var age:NSNumber?
    
        @objc var phones:[LYBPhonesModel]?
    
     init(dict:[String:Any]) {
    
            super.init()
    
             setValuesForKeys(dict)
    
        }
    
        override func setValue(_ value: Any?, forKey key: String) {
    
            
    
            if key=="phones"{
    
                let temp = value as! [AnyObject]
    
                    var resultArray = [LYBPhonesModel]()
    
                    for dict in temp {
    
                    resultArray.append(LYBPhonesModel(dict: dict as! [String : AnyObject]))
    
                
    
            }
    
                phones = resultArray
    
                return
    
                        }
    
            super.setValue(value, forKey: key)
    
        }
    
        override func setValue(_ value: Any?, forUndefinedKey key: String) {
    
        }
    
        
    
       
    
    }
    
    *****************第二层模型*********
    
    
    
    import UIKit
    
    
    
    class LYBPhonesModel: NSObject {
    
       @objc var name:String?
    
       @objc var number:String?
    
        
    
        init(dict: [String: AnyObject]) {
    
            super.init()
    
            
    
            setValuesForKeys(dict)
    
        }
    
        
    
        override func setValue(_ value: Any?, forKey key: String) {
    
            super.setValue(value, forKey: key)
    
        }
    
        override func setValue(_ value: Any?, forUndefinedKey key: String) {
    
            
    
        }
    
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    *****************
    
    
    
    //kvc
    
        func swiftkvc(){
    
         
    
    
    
     //1.这是一个JSON字符串
    
            let jsonStr = "[{\"name\": \"hangge\", \"age\": 100, \"phones\": [{\"name\": \"公司\",\"number\": \"123456\"}, {\"name\": \"家庭\",\"number\": \"001\"}]}, {\"name\": \"big boss\",\"age\": 1,\"phones\": [{ \"name\": \"公司\",\"number\": \"111111\"}]}]"
    
            
    
            //2.吧JSON字符串变成data数据
    
            if let jsonData = jsonStr.data(using: String.Encoding.utf8, allowLossyConversion: false) {
    
              
    
        //***************系统的序列化成json*************
    
                do{
    
    //                //3.吧jsondata转换成JSON对象(即字典,或字典数组)
    
                    if  let dictArr:[[String:Any]]=(tryJSONSerialization.jsonObject(with:jsonData , options: .allowFragments)as?[[String : Any]])  {
    
                        let dict:[String:Any]=dictArr[1]
    
                        let model:LYBHomeLunboModel=LYBHomeLunboModel(dict:dict)
    
                        
    
                        print("\(model.phones![0].name!)")
    
                    }
    
                }
    
            catch {
    
    
    
                }
    
            
    
    
    
        }
    
    
    

     

    ================================MJExtention桥接字典转模型================================


     

    
    1.这是一个JSON字符串
    
    //let jsonStr = "
    
    //[
    
    //{\"name\": \"hangge\",
    
    //\"age\": 100,
    
    //\"phones\":
    
    //    [
    
    //    {
    
    //        \"name\": \"公司\",
    
    //    \"number\": \"123456\"
    
    //
    
    //    },
    
    //    {
    
    //        \"name\": \"家庭\",
    
    //        \"number\": \"001\"
    
    //
    
    //    }
    
    //    ]
    
    //},
    
    //{
    
    //    \"name\": \"big boss\",
    
    //    \"age\": 1,
    
    //    \"phones\":
    
    //    [
    
    //    {
    
    //        \"name\": \"公司\",
    
    //    \"number\": \"111111\"
    
    //
    
    //    }
    
    //    ]
    
    //
    
    //}
    
    //    ]"
    
    
    
    #import <Foundation/Foundation.h>
    
    #import <MJExtension/MJExtension.h>
    
    @interface LYBmjextentionTofirstModel : NSObject
    
    @property(nonatomic,copy)NSString *name;
    
    @property(nonatomic,copy)NSString *age;
    
    @property(nonatomic,strong)NSArray *phones;
    
    @end
    
    
    
    ****************
    
    
    
    #import "LYBmjextentionTofirstModel.h"
    
    
    
    #import "LYBmjextentionModelTosec.h"
    
    @implementation LYBmjextentionTofirstModel
    
    + (NSDictionary *)mj_objectClassInArray
    
    
    
    {
    
        return@{@"phones" : [LYBmjextentionModelTosecclass]};
    
    }
    
    @end
    
    
    
    
    
    *******************
    
    
    
    
    
    #import <Foundation/Foundation.h>
    
    #import <MJExtension/MJExtension.h>
    
    @interface LYBmjextentionModelTosec : NSObject
    
    @property(nonatomic,copy)NSString *name;
    
    @property(nonatomic,copy)NSString *number;
    
    @end
    
    
    
    ************************
    
    
    
    
    
    
    
    #import "LYBmjextentionModelTosec.h"
    
    
    
    @implementation LYBmjextentionModelTosec
    
    
    
    @end
    
    
    
    **************************
    
    
    
    swift的桥接文件中
    
    #import <MJExtension/MJExtension.h>
    
    #import "LYBmjextentionTofirstModel.h"
    
    **************
    
    在VC中使用:
    
     //1.这是一个JSON字符串
    
            let jsonStr ="[{\"name\": \"hangge\", \"age\": 100, \"phones\": [{\"name\": \"公司\",\"number\": \"123456\"}, {\"name\": \"家庭\",\"number\": \"001\"}]}, {\"name\": \"big boss\",\"age\": 1,\"phones\": [{ \"name\": \"公司\",\"number\": \"111111\"}]}]"
    
            
    
            //2.吧JSON字符串变成data数据
    
            if let jsonData = jsonStr.data(using:String.Encoding.utf8, allowLossyConversion:false) {
    
    
    
    
    
    //**************swiftyjson*吧json的data数据序列化成json对象然后mjextention转模型****************不用担心数组越界,不用判断节点,拆包什么的
    
               
    
                do{//序列化成JSON对象
    
                    let json = try JSON(data: jsonData, options:.allowFragments)
    
                    let dict=json[0]
    
    
    
                   let fm=LYBmjextentionTofirstModel.mj_object(withKeyValues:dict)或者print(fm)
    
                    let model:LYBmjextentionTofirstModel=LYBmjextentionTofirstModel()
    
                    model.mj_setKeyValues(dict)
    
                    print(model)
    
                    print("\(model.name)")
    
                }catch{
    
                   
    
                }
    
                
    
            }
    

    ==================反射字典转模型=========

    参考:http://swift.gg/2015/11/23/swift-reflection-api-what-you-can-do/#custom_mirrors

    https://www.jianshu.com/p/3d0dba5f781a

     

    所谓反射就是可以动态获取类型、成员信息,同时在运行时(而非编译时)可以动态调用任意方法、属性等行为的特性。

    Swift的反射机制是基于一个叫Mirror的struct来实现的,其内部有如下属性和方法:

    
    let children: Children  //对象的子节点。
    
    displayStyle: Mirror.DisplayStyle?   //对象的展示风格
    
    let subjectType: Any.Type  //对象的类型
    
    func superclassMirror() -> Mirror?   //对象父类的
    
    
    
    基于上面mjextention中的模型来实现:
    
      let model:LYBmjextentionTofirstModel=LYBmjextentionTofirstModel()
    
                model.name="名字"
    
                    model.age="18"
    
                    //将model进行反射
    
                    let aMirror = Mirror(reflecting: model)
    
                    print("对象类型:\(aMirror.subjectType)")
    
                    print("对象子元素的个数:\(aMirror.children.count)")
    
                    print("对象的展示风格:\(aMirror.displayStyle)")
    
                   
    
          *******************
    
            
    

    =======================HandyJSON==============阿里巴巴出的

     

    下载地址:https://github.com/alibaba/handyjson

     

    参考:https://www.cnblogs.com/crazyacking/p/5927909.html?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io

    采用Swift反射+内存赋值的方式来构造Model实例,保持原汁原味的Swift类定义

    在Swift中把JSON反序列化到Model类,在HandyJSON出现以前,主要使用两种方式:

    1. 让Model类继承自NSObject,然后class_copyPropertyList()方法获取属性名作为Key,从JSON中取得Value,再通过Objective-C runtime支持的KVC机制为类属性赋值;如JSONNeverDie

    2. 支持纯Swift类,但要求开发者实现Mapping函数,使用重载的运算符进行赋值,如ObjectMapper

    这两者都有显而易见的缺点。前者要求Model继承自NSObject,非常不优雅,且直接否定了用struct来定义Model的方式;后者的Mapping函数要求开发者自定义,在其中指明每个属性对应的JSON字段名,代码侵入大,且仍然容易发生拼写错误、维护困难等问题。

    ***注意:swift4.0,在用cocoapods引入的时候,需要注明版本pod 'HandyJSON','4.0.0-beta.1'

     

    HandyJSON支持在类定义里使用各种形式的基本属性,包括可选(?),隐式解包可选(!),数组(Array),字典(Dictionary),Objective-C基本类型(NSString、NSNumber),各种类型的嵌套([Int]?、[String]?、[Int]!、...)等等

     

    使用举例:
    
    1.***********简单的json字符串(字符串中是纯json格式):
    
    
    import UIKit
    import HandyJSON
    class LYBHandyJsononeModel: HandyJSON {//模型类需要继承HandyJSON
        @objc var name:String?
        @objc var age:NSNumber?
        
     required init() {}// 如果定义是struct,连init()函数都不用声明;
    }
    
    
    
    
    使用:
    
     let jsonStr="{\"name\": \"hangge\", \"age\": 100}"
              
            //2.吧JSON字符串变成data数据
            if let jsonData = jsonStr.data(using: String.Encoding.utf8, allowLossyConversion: false) {
              
        //***************系统的序列化成json*************
                do{
    //                //3.吧jsondata转换成JSON对象(即字典,或字典数组)
                    if  let dict:[String:Any]=(try JSONSerialization.jsonObject(with:jsonData , options: .allowFragments)as?[String : Any])  {
                       let model=JSONDeserializer<LYBHandyJsononeModel>.deserializeFrom(dict: dict)
                        print((model?.name)!)
                    }
                }
            catch {
    
                }
            

    3.***************---json字符串中的最外层是数组

    import UIKit
    import HandyJSON
    class LYBHandyJsononeModel: HandyJSON {
        @objc var name:String?
        @objc var age:String?
        
     required init() {}// 如果定义是struct,连init()函数都不用声明;
    }

     

     使用:

     //1.这是一个JSON字符串,字符串中最外层是数组
            let jsonStr = "[{\"name\": \"hangge\", \"age\": 100, \"phones\": [{\"name\": \"公司\",\"number\": \"123456\"}, {\"name\": \"家庭\",\"number\": \"001\"}]}, {\"name\": \"big boss\",\"age\": 15,\"phones\": [{ \"name\": \"公司\",\"number\": \"111111\"}]}]"
            let model=JSONDeserializer<LYBHandyJsononeModel>.deserializeModelArrayFrom(json: jsonStr)
            print((model?[0]?.age)!)
    

    4.***********json字符串,最外层是数组,数组中的内容是字典

     

     

    import UIKit
    import HandyJSON
    class LYBHandyJsononeModel: HandyJSON {
        @objc var name:String?
        @objc var age:String?
        
     required init() {}// 如果定义是struct,连init()函数都不用声明;
    }
     //1.这是一个JSON字符串,字符串中最外层是数组
            let jsonStr = "[{\"name\": \"hangge\", \"age\": 100, \"phones\": [{\"name\": \"公司\",\"number\": \"123456\"}, {\"name\": \"家庭\",\"number\": \"001\"}]}, {\"name\": \"big boss\",\"age\": 15,\"phones\": [{ \"name\": \"公司\",\"number\": \"111111\"}]}]"
           

     

     

    使用:

    
     //2.吧JSON字符串变成data数据
            if let jsonData = jsonStr.data(using: String.Encoding.utf8, allowLossyConversion: false) {
              
        //***************系统的序列化成json*************
                do{
    //                //3.吧jsondata转换成JSON对象(即字典,或字典数组)
                    if  let dictArr:[[String:Any]]=(try JSONSerialization.jsonObject(with:jsonData , options: .allowFragments)as?[[String : Any]])  {
                       let model=JSONDeserializer<LYBHandyJsononeModel>.deserializeModelArrayFrom(array: dictArr)
                        print((model![0]?.name)!)
                    }
                }
            catch {
    
                }
            
     let model=JSONDeserializer<LYBHandyJsononeModel>.deserializeModelArrayFrom(array: dictArr)
                        print((model![0]?.name)!)
                    }
                }
            catch {
    
                }
            
    

    ===========字典数组转模型1=========

     /*
                 {
                 msg = "\U83b7\U53d6\U6210\U529f";
                 result =     {
                 ad =         (
                 {
                 "ad_code" = "http://www.ddb.cn/public/upload/ad/2018/07-24/f1278a99a637ff4db766e94339dec180.jpg";
                 "ad_link" = "https://mp.weixin.qq.com/s/x1JPnh5qXl0bkyvN5EcvrA";
                 "ad_name" = "\U4fe1\U7528\U5361\U57ab\U8fd8";
                 },
                 {
                 "ad_code" = "http://www.ddb.cn/public/upload/ad/2018/08-22/8b7a59c2924c1813f9e2c5895a02a226.png";
                 "ad_link" = "https://mp.weixin.qq.com/s/phLc3iHQaU6TyI6hkmfIpg";
                 "ad_name" = "\U4fe1\U7528\U5361\U529e\U7406";
                 },
                 {
                 "ad_code" = "http://www.ddb.cn/public/upload/ad/2018/11-09/52be0b76b5c4d579cbb3c88d146c6ea0.jpg";
                 "ad_link" = "https://mp.weixin.qq.com/s/sWM6q5Fi726Ruj73uFZVfg";
                 "ad_name" = "\U81ea\U5b9a\U4e49\U5e7f\U544a\U540d\U79f0";
                 }
                 );
                 };
                 status = 1;
                 }
    */
                if  ((res["result"]) != nil) {//判断res中是否存在result这个键
                let resultDict=res["result"] as! NSDictionary
                 if  ((res["ad"]) != nil) {
                    let ad:[[String:AnyObject]]=resultDict.value(forKey: "ad") as! [[String : AnyObject]]
                     print("\(ad.count)")
                   
                    let model=JSONDeserializer<LYBHomeHeaderScrollModel>.deserializeModelArrayFrom(array: ad);//字典数组转成模型数组方法一
    //                let headerScrollModel=[LYBHomeHeaderScrollModel].deserialize(from: ad)// 字典数组转换成模型数组方法二
                print("\(String(describing: model?[0]?.ad_link))")
                    }
                }else {
                    print("没有")
                }

    ============字典转模型==============

    if  ((res["result"]) != nil) {//判断res中是否存在result这个键
                let resultDict=res["result"] as? NSDictionary
                let  loginresultModel=LYBLoginResultModel.deserialize(from: resultDict)//这是根据字典转模型,还可以直接字符串转模型
                LYBLoginVC.model=loginresultModel//静态变量存储,整个程序生命周期期间且所有类中都可以用
                print("\(String(describing: loginresultModel?.token))")
            }else{
                print("没有")
            }

    ********嵌套转模型*******

     lazy var rightdata:[String:[[String:[String]]]]={
            let dataDict:[String:[[String:[String]]]]=["leftone":[["headerName":["one第一组"],"images":["icon_shot_sel","icon_shot_sel","icon_shot_sel","icon_shot_sel","icon_shot_sel","icon_shot_sel"]],["headerName":["one第二组"],"images":["comm_btn_checkmark","comm_btn_checkmark","comm_btn_checkmark","comm_btn_checkmark","comm_btn_checkmark","comm_btn_checkmark"]],["headerName":["one第三组"],"images":["comm_btn_checkmark","comm_btn_checkmark","comm_btn_checkmark","comm_btn_checkmark","comm_btn_checkmark","comm_btn_checkmark"]]],
                                                       "lefttwo":[["headerName":["two第一组"],"images":["icon_shot_sel","icon_shot_sel","icon_shot_sel","icon_shot_sel","icon_shot_sel","icon_shot_sel"]],["headerName":["two第二组"],"images":["comm_btn_checkmark","comm_btn_checkmark","comm_btn_checkmark","comm_btn_checkmark","comm_btn_checkmark","comm_btn_checkmark"]],["headerName":["two第三组"],"images":["comm_btn_checkmark","comm_btn_checkmark","comm_btn_checkmark","comm_btn_checkmark","comm_btn_checkmark","comm_btn_checkmark"]]],
                                                       "leftthree":[["headerName":["three第一组"],"images":["icon_shot_sel","icon_shot_sel","icon_shot_sel","icon_shot_sel","icon_shot_sel","icon_shot_sel"]],["headerName":["three第二组"],"images":["comm_btn_checkmark","comm_btn_checkmark","comm_btn_checkmark","comm_btn_checkmark","comm_btn_checkmark","comm_btn_checkmark"]],["headerName":["three第三组"],"images":["comm_btn_checkmark","comm_btn_checkmark","comm_btn_checkmark","comm_btn_checkmark","comm_btn_checkmark","comm_btn_checkmark"]]]
                         ]
            
            return dataDict
        }()
    
    
    *******第一层
    import UIKit
    
    class LYBTabAndCollectionConnectModel: HandyJSON {
        var leftone:[LYBrightSecModel]?
         var lefttwo:[LYBrightSecModel]?
         var leftthree:[LYBrightSecModel]?
        
        required init(){
            
        }
    }
    
    
    *******第二层
    import UIKit
    
    class LYBrightSecModel: HandyJSON {
    
        var headerName:[String]?
        var images:[String]?
        required init(){
        
        }
    }
    
    
    使用: let  modelDict=LYBTabAndCollectionConnectModel.deserialize(from: rightdata) as! LYBTabAndCollectionConnectModel
            print("\(String(describing: modelDict.leftone?.first?.images?.first))")
    

    5.*******************字典中的value是字典或者是数组(数组中的元素是字典),继续创建下一级模型

    嵌套的Model类

     

    如果Model类中的某个属性是另一个自定义的Model类,那么只要那个Model类也实现了HandyJSON协议,就一样可以转换:

     

    
    //第一层模型
    
    import UIKit
    
    import HandyJSON
    
    class LYBHandyJsononeModel: HandyJSON {
    
      var name:String?
    
    var age:String?
    
      var phones:[LYBHandyJsonSecModel]?
    
     required init() {}// 如果定义是struct,连init()函数都不用声明;
    
    }
    
    
    
    //第二层模型
    
    import UIKit
    
    import HandyJSON
    
    class LYBHandyJsonSecModel: HandyJSON {
    
        var name:String?
    
        var age:String?
    
        
    
        required init() {}// 如果定义是struct,连init()函数都不用声明;
    
    }
    
    
    
     //1.这是一个JSON字符串,字符串中最外层是数组
            let jsonStr = "[{\"name\": \"hangge\", \"age\": 100, \"phones\": [{\"name\": \"公司\",\"number\": \"123456\"}, {\"name\": \"家庭\",\"number\": \"001\"}]}, {\"name\": \"big boss\",\"age\": 15,\"phones\": [{ \"name\": \"公司\",\"number\": \"111111\"}]}]"
           
    [{\"name\": \"hangge\", \"age\": 100, \"phones\": [{\"name\": \"公司\",\"number\": \"123456\"}, {\"name\": \"家庭\",\"number\": \"001\"}]}, {\"name\": \"big boss\",\"age\": 15,\"phones\": [{ \"name\": \"公司\",\"number\": \"111111\"}]}]"
           
    

    使用:

     //2.吧JSON字符串变成data数据
    
    
            if let jsonData = jsonStr.data(using: String.Encoding.utf8, allowLossyConversion: false) {
              
        //***************系统的序列化成json*************
                do{
    //                //3.吧jsondata转换成JSON对象(即字典,或字典数组)
                    if  let dictArr:[[String:Any]]=(try JSONSerialization.jsonObject(with:jsonData , options: .allowFragments)as?[[String : Any]])  {
                      let model=JSONDeserializer<LYBHandyJsononeModel>.deserializeModelArrayFrom(array: dictArr)
                        let phones=model![0]?.phones;
                        print("\((phones![1].name)!)")
                    }
                }
            catch {
    
                }
              let model=JSONDeserializer<LYBHandyJsononeModel>.deserializeModelArrayFrom(array: dictArr)
                        let phones=model![0]?.phones;
                        print("\((phones![1].name)!)")
                    }
                }
            catch {
    
                }
    
    
    6.********************指定反序列化JSON中某个节点(指定路径转模型,可以选择有用的数组转模型,跳过无用的数据)------解析的指定路径要保证唯一性才可以;
    <span style="color:#ff0000">这是一个JSON字符串,字符串内只有字典嵌套,可以任意指定路径解析</span>

     

    有时候服务端返回给我们的JSON文本包含了大量的状态信息,和Model无关,可以指定反序列化哪个节点:

    有继承关系的Model类

    如果某个Model类继承自另一个Model类,只需要这个父Model类实现HandyJSON协议就可以:

    //第一层模型

    import UIKit

    import HandyJSON

    class LYBHandyJsononeModel: HandyJSON {

      var name:String?

    var age:String?

      var phones:LYBHandyJsonSecModel?

     required init() {}// 如果定义是struct,连init()函数都不用声明;

    }

    *******

    //第二层模型

    import UIKit

    import HandyJSON

    class LYBHandyJsonSecModel: HandyJSON {

        var name:String?

        required init() {}// 如果定义是struct,连init()函数都不用声明;

    }

     

    ***********

    //第三层模型

    import UIKit

    import HandyJSON

    class LYBthreemodel: HandyJSON {

        var age:String?

        required init() {}// 如果定义是struct,连init()函数都不用声明;

    }

     

     

    使用:

     //1.这是一个JSON字符串,字符串内只有字典嵌套,指定路径解析
            let jsonStr = "{\"name\": \"hangge\", \"age\": 100, \"phones\":{\"name\": {\"age\": 100}}}"
            let model=JSONDeserializer<LYBHandyJsononeModel>.deserializeFrom(json: jsonStr, designatedPath: "phones.name")
            print(model?.age)/1.这是一个JSON字符串,字符串内只有字典嵌套,指定路径解析
            let jsonStr = "{\"name\": \"hangge\", \"age\": 100, \"phones\":{\"name\": {\"age\": 100}}}"
            let model=JSONDeserializer<LYBHandyJsononeModel>.deserializeFrom(json: jsonStr, designatedPath: "phones.name")
            print(model?.age)


    7.**********************指定路径解析(字典的value值有数组,路径最多只能到数组的键值这里)

    //第一层模型
    import UIKit
    import HandyJSON
    class LYBHandyJsononeModel: HandyJSON {
      var name:String?
    var age:String?
      var phones:[LYBHandyJsonSecModel]?
     required init() {}// 如果定义是struct,连init()函数都不用声明;
    }
    

    //第二层模型

    import UIKit

    import HandyJSON

    class LYBHandyJsonSecModel: HandyJSON {

        var name:String?

        required init() {}// 如果定义是struct,连init()函数都不用声明;

    }

    //第三层模型

    import UIKit

    import HandyJSON

    class LYBthreemodel: HandyJSON {

        var age:String?

        required init() {}// 如果定义是struct,连init()函数都不用声明;

    }

     

     

     

      //1.这是一个JSON字符串,字典的value值是数组,指定路径解析
            let jsonStr = "{\"name\": \"hangge\", \"age\": 100, \"phones\":[{\"name\": {\"age\": 100}}]}"
            let model=JSONDeserializer<LYBHandyJsononeModel>.deserializeModelArrayFrom(json: jsonStr, designatedPath: "phones")//因为phones是数组(所以要使用deserializeModelArrayFrom()
    ),数组中元素可能有多个,无法确定唯一路径,这里只能指定到phones
            print(model)
     //1.这是一个JSON字符串,字典的value值是数组,指定路径解析
            let jsonStr = "{\"name\": \"hangge\", \"age\": 100, \"phones\":[{\"name\": {\"age\": 100}}]}"
            let model=JSONDeserializer<LYBHandyJsononeModel>.deserializeModelArrayFrom(json: jsonStr, designatedPath: "phones")//因为phones是数组(所以要使用deserializeModelArrayFrom()
    ),数组中元素可能有多个,无法确定唯一路径,这里只能指定到phones
            print(model)
    


    8.****************自定义解析方式*****************

     

     

    HandyJSON还提供了一个扩展能力,就是允许自行定义Model类某个字段的解析Key、解析方式。我们经常会有这样的需求:

    • 某个Model中,我们不想使用和服务端约定的key作为属性名,想自己定一个;
    • 有些类型如enumtuple是无法直接从JSON中解析出来的,但我们在Model类中有这样的属性;

    HandyJSON协议提供了一个可选的mapping()函数,我们可以在其中指定某个字段用什么Key、或者用什么方法从JSON中解析出它的值

    //第一层模型

    import UIKit

    import HandyJSON

    class LYBHandyJsononeModel: HandyJSON {

      var myname:String?

    var age:(String,String)?

      var phones:[LYBHandyJsonSecModel]?

     required init() {}// 如果定义是struct,连init()函数都不用声明;

        

        func mapping(mapper: HelpingMapper) {

            //指定name字段用myname去解析

           mapper.specify(property: &myname, name: "name")

            //指定age用这个方法解析返回的是元祖,age中是100/200---分成100和200组成元组

            mapper.specify(property: &age) { (rawString) -> (String, String) in

                let parentNames = rawString.split(separator: "/").map(String.init)//

                return (parentNames[0], parentNames[1])

            }

           

           

     

        }

        

    }

     

    //第二层模型

    import UIKit

    import HandyJSON

    class LYBHandyJsonSecModel: HandyJSON {

        var name:String?

        required init() {}// 如果定义是struct,连init()函数都不用声明;

    }

     

     

      //1.这是一个JSON字符串,解析的时候把100/200解析成一个元祖(100,200)
        let jsonStr = "{\"name\": \"hangge\", \"age\":\"100/200\", \"phones\":[{\"name\": {\"age\": 100}}]}"
            let model=JSONDeserializer<LYBHandyJsononeModel>.deserializeFrom(json: jsonStr, designatedPath: "")//路径是空的话,意思和不带路径的方法一个效果
            print(model?.age)解析的时候把100/200解析成一个元祖(100,200)
        let jsonStr = "{\"name\": \"hangge\", \"age\":\"100/200\", \"phones\":[{\"name\": {\"age\": 100}}]}"
            let model=JSONDeserializer<LYBHandyJsononeModel>.deserializeFrom(json: jsonStr, designatedPath: "")//路径是空的话,意思和不带路径的方法一个效果
            print(model?.age)


    9.****************

     

    展开全文
  • key-path 通常是用在键值编码(KVC)与键值观察(KVO)上的,KVC、KVO 相关内容可以参考我之前写的这篇文章:Swift - 反射(Reflection)的介绍与使用样例(附KVC介绍) 1.Swift 3 之前使用的是 String 类型的 key-...

    一、Key Paths 新语法

    key-path 通常是用在键值编码(KVC)与键值观察(KVO)上的,KVC、KVO 相关内容可以参考我之前写的这篇文章:Swift - 反射(Reflection)的介绍与使用样例(附KVC介绍)

    1.Swift 3 之前使用的是 String 类型的 key-Path

    //用户类
    class User: NSObject{
        @objc var name:String = ""  //姓名
        @objc var age:Int = 0  //年龄
    }
     
    //创建一个User实例对象
    let user1 = User()
    user1.name = "hangge"
    user1.age = 100
     
    //使用KVC取值
    let name = user1.value(forKey: "name")
    print(name)
     
    //使用KVC赋值
    user1.setValue("hangge.com", forKey: "name")
    复制代码

    具体显示如下:

    2.到了 Swift 3 新增了 #keyPath() 写法 使用 #keyPath() 写法,可以避免我们因为拼写错误而引发问题。

    //用户类
    class User: NSObject{
        @objc var name:String = ""  //姓名
        @objc var age:Int = 0  //年龄
    }
     
    //创建一个User实例对象
    let user1 = User()
    user1.name = "hangge"
    user1.age = 100
     
    //使用KVC取值
    let name = user1.value(forKeyPath: #keyPath(User.name))
    print(name)
     
    //使用KVC赋值
    user1.setValue("hangge.com", forKeyPath: #keyPath(User.name))
    复制代码

    3.Swift 4 中直接用 \ 作为开头创建 KeyPath 新的方式不仅使用更加简单,而且有如下优点:

    • 类型可以定义为 class、struct
    • 定义类型时无需加上 @objc 等关键字
    • 性能更好
    • 类型安全和类型推断,例如:user1.value(forKeyPath: #keyPath(User.name)) 返回的类型是 Any,user1[keyPath: \User.name] 直接返回 String 类型
    • 可以在所有值类型上使用 (1)比如上面的样例在 Swift4 中可以这么写:
    //用户类
    class User: NSObject{
        var name:String = ""  //姓名
        var age:Int = 0  //年龄
    }
     
    //创建一个User实例对象
    let user1 = User()
    user1.name = "hangge"
    user1.age = 100
     
    //使用KVC取值
    let name = user1[keyPath: \User.name]
    print(name)
     
    //使用KVC赋值
    user1[keyPath: \User.name] = "hangge.com"
    复制代码

    (2)keyPath 定义在外面也是可以的:

    let keyPath = \User.name
     
    let name = user1[keyPath: keyPath]
    print(name)
     
    user1[keyPath: keyPath] = "hangge.com"
    复制代码

    (3)可以使用 appending 方法向已定义的 Key Path 基础上填加新的 Key Path。

    let keyPath1 = \User.phone
    let keyPath2 = keyPath1.appending(path: \.number)
    复制代码

    二、类与协议的组合类型

    在 Swift 4 中,可以把类(Class)和协议(Protocol)用 & 组合在一起作为一个类型使用。

    使用样例1:

    protocol MyProtocol { }
     
    class View { }
     
    class ViewSubclass: View, MyProtocol { }
     
    class MyClass {
        var delegate: (View & MyProtocol)?
    }
     
    let myClass = MyClass()
    myClass.delegate = ViewSubclass() //这个编译正常
    myClass.delegate = View() //这个编译报错:
    复制代码

    具体错误信息如下:

    使用样例2:

    protocol Shakeable {
        func shake()
    }
     
    extension UIButton: Shakeable {
        func shake() {
            /* ... */
        }
    }
     
    extension UISlider: Shakeable {
        func shake() {
            /* ... */
        }
    }
     
    func shakeEm(controls: [UIControl & Shakeable]) {
        for control in controls where control.isEnabled {
            control.shake()
        }
    }
    复制代码

    三、下标支持泛型

    1.下标的返回类型支持泛型 有时候我们会写一些数据容器,Swift 支持通过下标来读写容器中的数据。但是如果容器类中的数据类型定义为泛型,过去下标语法就只能返回 Any,在取出值后需要用 as? 来转换类型。现在 Swift 4 定义下标也可以使用泛型了。

    struct GenericDictionary<Key: Hashable, Value> {
        private var data: [Key: Value]
         
        init(data: [Key: Value]) {
            self.data = data
        }
         
        subscript<T>(key: Key) -> T? {
            return data[key] as? T
        }
    }
     
    //字典类型: [String: Any]
    let earthData = GenericDictionary(data: ["name": "Earth", "population": 7500000000, "moons": 1])
     
    //自动转换类型,不需要在写 "as? String"
    let name: String? = earthData["name"]
    print(name)
     
    //自动转换类型,不需要在写 "as? Int"
    let population: Int? = earthData["population"]
    print(population)
    复制代码

    2.下标类型同样支持泛型

    extension GenericDictionary {
        subscript<Keys: Sequence>(keys: Keys) -> [Value] where Keys.Iterator.Element == Key {
            var values: [Value] = []
            for key in keys {
                if let value = data[key] {
                    values.append(value)
                }
            }
            return values
        }
    }
     
    // Array下标
    let nameAndMoons = earthData[["moons", "name"]]        // [1, "Earth"]
    // Set下标
    let nameAndMoons2 = earthData[Set(["moons", "name"])]  // [1, "Earth"]
    复制代码

    四、Codable 序列化

    如果要将一个对象持久化,需要把这个对象序列化。过去的做法是实现 NSCoding 协议,但实现 NSCoding 协议的代码写起来很繁琐,尤其是当属性非常多的时候。 Swift 4 中引入了 Codable 协议,可以大大减轻了我们的工作量。我们只需要让需要序列化的对象符合 Codable 协议即可,不用再写任何其他的代码。

    struct Language: Codable {
        var name: String
        var version: Int
    }
    复制代码

    1.Encode 操作 我们可以直接把符合了 Codable 协议的对象 encode 成 JSON 或者 PropertyList。

    let swift = Language(name: "Swift", version: 4)
     
    //encoded对象
    let encodedData = try JSONEncoder().encode(swift)
     
    //从encoded对象获取String
    let jsonString = String(data: encodedData, encoding: .utf8)
    print(jsonString)
    复制代码

    2.Decode 操作

    let decodedData = try JSONDecoder().decode(Language.self, from: encodedData)
    print(decodedData.name, decodedData.version)
    复制代码


    五、Subtring

    Swift 4 中有一个很大的变化就是 String 可以当做 Collection 来用,并不是因为 String 实现了 Collection 协议,而是 String 本身增加了很多 Collection 协议中的方法,使得 String 在使用时看上去就是个 Collection。

    let str = "hangge.com"
     
    print(str.prefix(5)) // "hangg"
    print(str.suffix(5)) // "e.com"
     
    print(str.dropFirst()) // "angge.com"
    print(str.dropLast()) // "hangge.co"
    复制代码

    比如上面的样例,我们使用一些 Collection 协议的方法对字符串进行截取,只不过它们的返回结果不是 String 类型,而是 Swift 4 新增的 Substring 类型。

    1.为何要引入 Substring? 既然我们想要的到的就是字符串,那么直接返回 String 就好了,为什么还要多此一举返回 Substring。原因只有一个:性能。具体可以参考下图: 当我们用一些 Collection 的方式得到 String 里的一部分时,创建的都是 Substring。Substring 与原 String 是共享一个 Storage。这意味我们在操作这个部分的时候,是不需要频繁的去创建内存,从而使得 Swift 4 的 String 相关操作可以获取比较高的性能。 而当我们显式地将 Substring 转成 String 的时候,才会 Copy 一份 String 到新的内存空间来,这时新的 String 和之前的 String 就没有关系了。

    2.使用 Substring 的注意事项 由于 Substring 与原 String 是共享存储空间的,只要我们使用了 Substring,原 String 就会存在内存空间中。只有 Substring 被释放以后,整个 String 才会被释放。 而且 Substring 类型无法直接赋值给需要 String 类型的地方,我们必须用 String() 包一层。当然这时系统就会通过复制创建出一个新的字符串对象,之后原字符串就会被释放。

    3.使用样例 这里对 String 进行扩展,新增一个 subString 方法。直接可以根据起始位置(Int 类型)和需要的长度(Int 类型),来截取出子字符串。

    extension String {
        //根据开始位置和长度截取字符串
        func subString(start:Int, length:Int = -1) -> String {
            var len = length
            if len == -1 {
                len = self.count - start
            }
            let st = self.index(startIndex, offsetBy:start)
            let en = self.index(st, offsetBy:len)
            return String(self[st ..< en])
        }
    }
    复制代码

    使用样例:

    let str1 = "欢迎访问hangge.com"
    let str2 = str1.subString(start: 4, length: 6)
    print("原字符串:\(str1)")
    print("截取出的字符串:\(str2)")
    复制代码

    运行结果如下:

    注意:这个方法最后我们会将 Substring 显式地转成 String 再返回。


    六、废除 swap 方法

    (1)过去我们会使用 swap(::) 来将两个变量的值进行交换:

    var a = 1
    var b = 2
    swap(&a, &b)
    print(a, b)
    复制代码

    (2)后面 swap() 方法将会被废弃,建议使用 tuple(元组)特性来实现值交换,也只需要一句话就能实现:

    var a = 1
    var b = 2
    (b, a) = (a, b)
    print(a, b)
    复制代码

    使用 tuple 方式的好处是,多个变量值也可以一起进行交换:

    var a = 1
    var b = 2
    var c = 3
    (a, b, c) = (b, c, a)
    print(a, b, c)
    复制代码

    (3)补充一下:现在数组增加了个 swapAt 方法可以实现两个元素的位置交换。

    var fruits = ["apple", "pear", "grape", "banana"]
    //交换元素位置(第2个和第3个元素位置进行交换)
    fruits.swapAt(1, 2)
    print(fruits)
    复制代码


    七、减少隐式 @objc 自动推断

    1.过去的情况(Swift 3) (1)在项目中如果想把 Swift 写的 API 暴露给 Objective-C 调用,需要增加 @objc。在 Swift 3 中,编译器会在很多地方为我们隐式的加上 @objc。 (2)比如当一个类继承于 NSObject,那么这个类的所有方法都会被隐式的加上 @objc。

    class MyClass: NSObject {
        func print() { } // 包含隐式的 @objc
        func show() { } // 包含隐式的 @objc
    }
    复制代码

    (3)但这样做很多并不需要暴露给 Objective-C 也被加上了 @objc。而大量 @objc 会导致二进制文件大小的增加。

    2.现在的情况(Swift 4) (1)在 Swift 4 中隐式 @objc 自动推断只会发生在下面这种必须要使用 @objc 的情况:

    • 覆盖父类的 Objective-C 方法
    • 符合一个 Objective-C 的协议

    (2)大多数地方必须手工显示地加上 @objc。

    class MyClass: NSObject {
        @objc func print() { } //显示的加上 @objc
        @objc func show() { } //显示的加上 @objc
    }
    复制代码

    (3)如果在类前加上 @objcMembers,那么它、它的子类、扩展里的方法都会隐式的加上 @objc。

    @objcMembers
    class MyClass: NSObject {
        func print() { } //包含隐式的 @objc
        func show() { } //包含隐式的 @objc
    }
     
    extension MyClass {
        func baz() { } //包含隐式的 @objc
    }
    复制代码

    (4)如果在扩展(extension)前加上 @objc,那么该扩展里的方法都会隐式的加上 @objc。

    class SwiftClass { }
     
    @objc extension SwiftClass {
        func foo() { } //包含隐式的 @objc
        func bar() { } //包含隐式的 @objc
    }
    复制代码

    (5)如果在扩展(extension)前加上 @nonobjc,那么该扩展里的方法都不会隐式的加上 @objc。

    @objcMembers
    class MyClass : NSObject {
        func wibble() { } //包含隐式的 @objc
    }
     
    @nonobjc extension MyClass {
        func wobble() { } //不会包含隐式的 @objc
    }
    复制代码

    ? 联系

    展开全文
  • swiftkvc

    2018-02-01 10:43:47
    因此,在使用KVC方法之前,需要确保对象已经被正确实例化 在Swift中,如果属性是可选的,在初始化时,不会为该属性分配空间。 在OC中,基本数据类型就是保存一个值,不存在可选的概念 所以我们需要给可选...
  • swift3.0转4.0遇到的坑

    千次阅读 2018-05-15 10:38:27
    我们以斗鱼APP为例,总结swift3.0以及swift4.0转换过程中遇到的问题。 一、方法重写问题 1、swift 3.0在子类中的方法重写 我们RecommendVC类继承自BaseAnchorVC,BaseAnchorVC中包含setupUI方法,我们重写...
  • 这几天苹果在开 WWDC2017 大会,期间放出了 Xcode9.0-beta 以及 Swift4... 如何适配Xcode9.0-beta 内置的 Swift 版本不止一个,它同时支持 Swift4.0Swift3.2 。而我们正在用的 Xcode8 ,最高只支持 Swift3.1 。基于
  • //// ViewController.swift// 002-函数的定义//// Created by 庄壮勇 on 2018/1/5.// Copyright © 2018年 Personal. All rights reserved.//import UIKitclass ViewController: UIViewController { override ...
  • Swift - KVO初探

    2019-02-28 00:58:51
    swift 4.0 KVO使用 swift4.0使用了不同于OC的API,使用起来更加简单。但是也需要注意几点: 是 swift class 需要在声明的时候增加 @objcMembers 关键字 被观察的属性需要用dynamic修饰 不需要在对象被回收时手动...
  • 1.首先创建一个Person的类 继承 ...// Person.swift // 008-构造函数(KVC) // // Created by 庄壮勇 on 2018/1/9. // Copyright © 2018年 Personal. All rights reserved. // import UIKit /**  1. 定义属性的时
  • ios -- 教你如何轻松学习Swift语法

    千次阅读 2016-08-21 17:44:37
    目前随着公司开发模式的变更,swift也显得越发重要,相对来说,swift语言更加简洁,严谨.但对于我来说,感觉swift细节的处理很繁琐,可能是还没适应的缘故吧.基本每写一句代码,都要对变量的数据类型进行判断,还要进行强转...
  • 该class需要继承NSObject,才可以使用KVC方法,通过key拿到值或者通过key赋值。这是一个牺牲,不想继承的使用UserDafult方法去存储,毕竟Swift里面少点oc东西好 如果想通过运行时一次获得所有属性列表,更方便的...
  • Swift4.0 Codable协议:JSON和模型的转换

    万次阅读 2020-04-20 16:20:40
    在OC中,以及Swift4.0之前,系统一直没有一套数据解析的方法。在Swift4.0后,终于推出了Codable协议,可实现json数据和数据模型的相互转换。 首先来下 Codable ,它其实是一个组合协议,有 Decodable 和 Encodable ...
  • 1.命名空间 在同一个项目中,所有的类都是共享的,可以直接访问,...Swift支持函数重载,所有构造函数都是init 作用: 分配空间,设置初始值 ** 注意 Object-c的初始化,是从类继承关系链,从父类开始逐级向子类初始化...
  • swift4.0语法杂记

    2019-01-03 17:20:24
    001--swift简史小视频 002--Playground体验 003--常量&amp;变量 一、swift简史 1、介绍   swift是苹果公司于2014年推出用于撰写OS和iOS应用程序的语言。它由苹果开发者工具部门总监“克里斯.拉特纳”在...
  • 再来一个小知识: 在Swift中,类方法里是不允许定义静态变量的 一:构造方法 知识点:Swift中,如果定义属性的时候没有初始化,那么必须在后面加上 “ ?”。但是只要在构造方法中,对属性进...
  • 001--swift简史小视频 002--Playground体验 003--常量&变量 一、swift简史 1、介绍 swift是苹果公司于2014年推出用于撰写OS和iOS应用程序的语言。它由苹果开发者工具部门总监“克里斯.拉特纳”在2010年开始着手...
  • swift Codable的使用及简单封装

    千次阅读 2018-08-22 11:55:44
    swift4.0之前,一直没有一套数据解析的方法。现在4.0后,终于有了Codable可以直接将json转成对象,有望取代OC的KVC机制。 先来看看Codable public typealias Codable = Decodable & Encodable 基本...
  • Swift4.0开发笔记

    千次阅读 2017-11-16 23:07:13
    目录 1、ATS配置 2、状态栏全局变亮白 3、懒加载 4、内存泄漏 5、常用的第三方库 6、反射机制 7、运行时 8、MD5加密 9、聊天界面 10、多线程GCD 11、 启动图尺寸大小 ... NSAllowsArbitr
  • Runtime在Swift中的用法

    2019-07-24 15:34:02
    Runtime是什么? 运行时(Runtime)是指将数据类型的确定由编译时推迟到了运行时 Runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API 平时编写的OC代码,在程序运行过程中,其实最终会...
1 2 3 4 5 ... 8
收藏数 152
精华内容 60
关键字:

4.0 kvc用法 swift