美文网首页iOS Developer
用Runtime做点什么(一)

用Runtime做点什么(一)

作者: Swifter丶 | 来源:发表于2017-03-08 00:06 被阅读60次

    前言

    网上关于runtime的教程都有很多,但时很多大部分都是讲解原理,并没有实际运用runtime写一些东西,所以很难让人理解。我觉得要是有实际的运用的话,应该能够更深刻的理解。

    先看一个效果

    图-1.png

    图-1 是我写的代码,我们假装json时从网络上取来的数据 , 然后把json的值赋给ViewController的model , model 是通过ModelCompatible这个协议得来的 ,如图-2


    图-2

    ModelCompatible有一个属性和一个方法,但凡有继承来这个属性的类(ps : 只有类,虽然swift中的enum struct也能继承协议。但是我们这个协议的后面是继承了一个class的关键字,表示这个协议就只能被类继承了)

    这个json里面的key值都和ViewController的属性相同,当把json赋值给model的时候,就会相应地改变ViewController的里面的属性的值,


    图-3 图-4

    这里打印了它的id,id也自动也被自动赋了值
    这样写的好处是,能大大减少我们的代码,而且使得代码的可读性更高。

    思路

    先通过runtime遍历一个的类的所有属性(不包括父类),然后和model里面的key比较,如果这个属性和key一样,在比较这个属性的类型,如果是String , Int , float 之类的,就把它们的json的key对应的值直接赋值给ViewController的属性的值。使用下面这个方法赋值。

    setValue(value: Any?, forKey: String)
    

    如果不是上面那个类型,那么就赋值给它的text的路径的属性下(因为所有的label , textField 都有text这个属性),使用下面的代码

    setValue(value: Any? , forKeyPath:  String)
    

    如果这个属性没有text的属性会怎么办,会崩溃。所以,前面使用了协议的方法,让拥有了这个协议的viewcontroller才会拥有这个runtime的小技巧。如果是扩展到所有UIViewController及它的子类下,就有可能出现一些不可控的崩溃。

    最后扩展下UImageView给它添加text的属性。

    图-5

    所有json要映射到UIViewController里面的视图属性上来显示,一般都是label, textfield , imageView (imageview是没有text的,所以要扩展一个) 。所以下面的代码直接ctrl + c 就可以用了。但是,最好是懂runtime的前提下去使用。这些主要是分享给那些懂runtime,但是不知道怎么在项目里运用的人。不懂runtime的要先看懂别人的runtime的文章在来看这个。

    
    import UIKit
    
    extension NSObject {
        
        var allKeys : [String] {
            
            var returnArr = [String]()
            
            var outCount : UInt32 = 0
            let members = class_copyPropertyList(self.classForCoder, &outCount)
            
            for i in 0..<numericCast(outCount) {
                
                autoreleasepool{
                    
                    let member = members![i]
                    let property = property_getName(member)
                    
                    
                    let propertyName = String.init(cString: property!)
                    
                    returnArr.append(propertyName)
                }
            }
            
            return returnArr
        }
        
    }
    
    public protocol ModelCompatible : class {
        var model : Any? {set get}
        func render()
    }
    
    fileprivate var modelkey : Void!
    public extension ModelCompatible {
        
        public var model : Any? {
            get{
                return  objc_getAssociatedObject(self, &modelkey)
            }
            set{
                objc_setAssociatedObject(self, &modelkey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
                //每次赋值model后就调用这个方法
                render()
            }
        }
        
        func render() {
            
                //因为要用到runtime , 所以要转成OC的NSObject
                if let objself = self as? NSObject {
                    
                    var outCount : UInt32 = 0
                    //我选择用property而不是ivar,是因为property足够用了
                    let members = class_copyPropertyList(objself.classForCoder, &outCount)
                    
                    for i in 0..<numericCast(outCount) {
                        
                        autoreleasepool{
                            
                            let member = members![i]
                            let property = property_getName(member)
                            let attribute = property_getAttributes(member)
                            
                            //通过runtime获取到了属性名
                            let propertyName = String.init(cString: property!)
                            
                            //通过runtime获取到了类型名
                            let typeName     = String(cString: attribute!).components(separatedBy: ",").first as NSString!
                            let typeStr = typeName!.substring(from: 1)
                            
                            
                            //将模型转为NSObject类 ,Dictionary也可以转成NSObject
                            if let dic = self.model as? NSObject {
                                
                                
                                if dic.allKeys.contains(propertyName){
                                    
                                    if typeStr == "@\"NSString\"" || typeStr == "q" || typeStr == "d" || typeStr == "d"{
                                        //kvc赋值
                                        objself.setValue(dic.value(forKey: propertyName)!, forKey: propertyName)
                                        
                                    }else{
                                        
                                        //kvc 赋值在一些类属性下的text属性下 , 
                                        //如果这个类没有text,就会崩溃。
                                        objself.setValue(String(describing: dic.value(forKey: propertyName)!), forKeyPath: propertyName + ".text")
                                        
                                    }
                                }
                            }
                        }
                    }
                    free(members)
                }
        }
    }
    
    extension UIImageView {
        var text: String {
            set{
                print("把获取到的url:", newValue , "转成图片")
                /**
                 比如: self.kf.setImage(with : url)
                */
            }
            get{
                return "没有get方法"
            }
        }
    }
    
    

    相关文章

      网友评论

        本文标题:用Runtime做点什么(一)

        本文链接:https://www.haomeiwen.com/subject/nyzmgttx.html