iOS Developer

用Runtime做点什么(一)

2017-03-08  本文已影响60人  Swifter丶

前言

网上关于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方法"
        }
    }
}

上一篇 下一篇

猜你喜欢

热点阅读