美文网首页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做点什么(一)

    前言 网上关于runtime的教程都有很多,但时很多大部分都是讲解原理,并没有实际运用runtime写一些东西,所...

  • 用Runtime做点什么(二)

    前言 其实这篇文章的重点并不在于runtime,只是我选择了用runtime扩展,而不是继承一个类的方法来实现的。...

  • OC运行时(runtime)总结 -- 类与对象

    什么是runtime? runtime 是OC底层的一套C语言库 (),基本上是用...

  • 二、runtime

    二、runtime 1.是什么 runtime提供了一套用c语言编写的底层API供开发者调用; runtime是一...

  • Runtime

    https://github.com/starainDou 欢迎点星 Runtime简介 runtime是什么(原...

  • Runtime使用场景 - 序列化和反序列化

    Runtime 有什么用? 利用Runtime运行时,在程序的运行过程中,动态创建一个类 利用Runtime运行时...

  • iOS 开发之runtime使用小结

    我们一般用runtime做以下这些事情: 一、使用runtime如何交换两个方法的实现,拦截系统自带的方法调用功能...

  • Runtime 见闻整理

    Runtime 基本是用C和汇编写的Runtime 涉及三个点,面向对象,消息分发,消息转发 面向对象 Objec...

  • iOS Method Swizzle黑魔法小记

    本文主要内容为用runtime实现Swizzle,即调换两个方法的实现 一点iOS runtime的基本知识 Ob...

  • 【OC梳理】runtime

    什么是runtime runtime是属于OC的底层,可以进行一些非常底层的操作(用OC是无法现实的, 不好实现)...

网友评论

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

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