美文网首页iOS Developer
通过实例了解KVC 、 Runtime

通过实例了解KVC 、 Runtime

作者: 强子ly | 来源:发表于2017-07-26 22:18 被阅读317次

    这个例子是我在网上看到的,主要介绍了用KVC和Runtime更改UITextField默认提示文字(PlaceHolder)颜色,借此机会简单介绍一下KVC和Runtime。

    874828-519b69d9089bf83c.gif

    目录:

    一、KVC (Key-value coding)键值编码

    1.1)、简介

    通过Key名直接访问对象的属性,或者给对象的属性赋值。而不需要调用明确的存取方法。这样就可以在运行
    时动态在访问和修改对象的属性。而不是在编译时确定,这也是iOS开发中的黑魔法之一。
    
    通常我们使用valueForKey 来替代getter 方法,setValue:forKey来代替setter方法。
    

    1.2)、常用方法

     //根据key取值
    - (nullable id)valueForKey:(NSString *)key;                         
    //根据key赋值. 
    - (void)setValue:(nullable id)value forKey:(NSString *)key;          
    //根据keyPath取值 
    - (nullable id)valueForKeyPath:(NSString*)keyPath;                   
    //根据keyPath赋值
    - (void)setValue:(nullable id)value forKeyPath:(NSString*)keyPath;   
    

    1.3)、使用场景

    场景一:单层字典模型转化(字典转model)

    [self.loginModel setValuesForKeysWithDictionary:resultDic];
    

    场景二:(iOS黑魔法) 通过KVC拿到控件API未暴露的属性,自定义修改

    例:通过KVC拿到UITextField的占位label,修改颜色
    
    UILabel *placeholderLabel=[self.userTextField valueForKeyPath:@"placeholderLabel"];
    placeholderLabel.textColor = [UIColor redColor];
    

    场景三:valueForKeyPath多用途

    1、使用valueForKeyPath可以获取数组中的最小值、最大值、平均值、求和
    
    CGFloat sum = [[array valueForKeyPath:@"@sum.floatValue"] floatValue];
    CGFloat avg = [[array valueForKeyPath:@"@avg.floatValue"] floatValue];
    CGFloat max =[[array valueForKeyPath:@"@max.floatValue"] floatValue];
    CGFloat min =[[array valueForKeyPath:@"@min.floatValue"] floatValue];
    
    2、数组内部去重(distinctUnionOfObjects)
    
    [dataArray valueForKeyPath:@"@distinctUnionOfObjects.self"]
    
    3、数组合并(去重合并:distinctUnionOfArrays.self、直接合并:unionOfArrays.self)
    
    NSArray *temp1 = @[@3, @2, @2, @1];
    NSArray *temp2 = @[@3, @4, @5];
    
    NSLog(@"\n%@",[@[temp1, temp2] valueForKeyPath:@"@distinctUnionOfArrays.self"]);
    NSLog(@"\n%@",[@[temp1, temp2] valueForKeyPath:@"@unionOfArrays.self"]);
    
    输出两个数组:( 5, 1, 2, 3, 4 ), ( 3, 2, 2, 1, 3, 4, 5 )。
    
    4、大小写转换(uppercaseString)及 打印字符串长度同样适用(length)
    
    NSArray *array = @[@"name", @"w", @"aa", @"jimsa"];
    NSLog(@"%@", [array valueForKeyPath:@"uppercaseString"]);
    打印:
    (NAME,W,AA,JIMSA)
    
    这里只挑几个介绍一下,下一步等你自己实践吧
    

    二、Runtime

    2.1)、简介

    runtime是一套底层的C语言API,包含很多强大实用的C语言数据类型和C语言函数,平时我们编写的OC代码,
    底层都是基于runtime实现的。
    
    作用:能动态产生、修改、删除一个类,一个成员变量,一个方法。
    

    2.2)、常用方法

    常用头文件:
    #import <objc/runtime.h> 包含对类、成员变量、属性、方法的操作
    #import <objc/message.h> 包含消息机制
    
    常用方法:
    class_copyIvarList()     返回一个指向类的成员变量数组的指针
    class_copyPropertyList() 返回一个指向类的属性数组的指针
    
    一般项目中使用:
    利用遍历类的属性,来快速的进行归档操作。
    将从后台返回的json数据进行字典模型转换。
    

    2.3)、使用场景

    1、打印所有成员变量
    - (void)getMemberVariables
    {
        unsigned int count = 0;
        
        //拷贝出所有的成员变量列表
        Ivar *ivars = class_copyIvarList([UITextField class], &count);
        
        for (int i = 0; i<count; i++)
        {
            // 取出成员变量
            Ivar ivar = *(ivars + i);
            // 打印成员变量名字
            NSLog(@"%s", ivar_getName(ivar));
            // 打印成员变量的数据类型
            NSLog(@"%s", ivar_getTypeEncoding(ivar));
        }
        //释放
        free(ivars);
    }
    

    打印成员变量结果

    KVC-RunTime[7226:235692] _textStorage
    KVC-RunTime[7226:235692] @"_UICascadingTextStor
    KVC-RunTime[7226:235692] _borderStyle
    KVC-RunTime[7226:235692] q
    KVC-RunTime[7226:235692] _minimumFontSize
    KVC-RunTime[7226:235692] d
    KVC-RunTime[7226:235692] _delegate
    KVC-RunTime[7226:235692] @
    KVC-RunTime[7226:235692] _background
    KVC-RunTime[7226:235692] @"UIImage"
    KVC-RunTime[7226:235692] _disabledBackground
    KVC-RunTime[7226:235692] @"UIImage"
    KVC-RunTime[7226:235692] _clearButtonMode
    KVC-RunTime[7226:235692] q
    KVC-RunTime[7226:235692] _leftView
    KVC-RunTime[7226:235692] @"UIView"
    KVC-RunTime[7226:235692] _leftViewMode
    KVC-RunTime[7226:235692] q
    KVC-RunTime[7226:235692] _rightView
    KVC-RunTime[7226:235692] @"UIView"
    KVC-RunTime[7226:235692] _rightViewMode
    KVC-RunTime[7226:235692] q
    KVC-RunTime[7226:235692] _traits
    KVC-RunTime[7226:235692] @"UITextInputTraits"
    KVC-RunTime[7226:235692] _nonAtomTraits
    KVC-RunTime[7226:235692] @"UITextInputTraits"
    
    2、获取所有属性的类名
    - (void)getSonClassIvar
    {
        unsigned int methCount = 0;
        Method *meths = class_copyMethodList([UITextField class], &methCount);
        
        for(int i = 0; i < methCount; i++)
        {
            Method meth = meths[i];
            SEL sel = method_getName(meth);
            const char *name = sel_getName(sel);
            NSLog(@"%s", name);
        }  
        free(meths);
    }
    

    打印结果

    KVC-RunTime[7279:238641] bs_setPlaceholder:
    KVC-RunTime[7279:238641] placeholderColor
    KVC-RunTime[7279:238641] setPlaceholderColor:
    KVC-RunTime[7279:238641] observeValueForKeyPath:ofObje
    KVC-RunTime[7279:238641] setProgress:
    KVC-RunTime[7279:238641] .cxx_destruct
    KVC-RunTime[7279:238641] dealloc
    KVC-RunTime[7279:238641] setAttributes:range:
    KVC-RunTime[7279:238641] setEnabled:
    KVC-RunTime[7279:238641] setDelegate:
    KVC-RunTime[7279:238641] methodSignatureForSelector:
    KVC-RunTime[7279:238641] forwardingTargetForSelector:
    KVC-RunTime[7279:238641] layoutSubviews
    KVC-RunTime[7279:238641] _populateArchivedSubviews:
    KVC-RunTime[7279:238641] hitTest:withEvent:
    KVC-RunTime[7279:238641] _intrinsicSizeWithinSize:
    KVC-RunTime[7279:238641] traitCollectionDidChange:
    KVC-RunTime[7279:238641] _backgroundView
    KVC-RunTime[7279:238641] gestureRecognizerShouldBegin:
    KVC-RunTime[7279:238641] setSemanticContentAttribute:
    KVC-RunTime[7279:238641] setTextAlignment:
    KVC-RunTime[7279:238641] setAttributedText:
    

    三、Demo实现

    3.1)、运用KVC实现动图效果

    - (void)viewDidLoad {
        [super viewDidLoad];
        
        self.view.backgroundColor = [UIColor grayColor];
        
        UITextField *userTextField = [[UITextField alloc] initWithFrame:CGRectMake(30, 100, 375, 40)];
        userTextField.placeholder = @"请输入账号";
        userTextField.tag = 101;
        [userTextField addTarget:self action:@selector(textEditBegin:) forControlEvents:UIControlEventEditingDidBegin];
        [userTextField addTarget:self action:@selector(textEditEnd:) forControlEvents:UIControlEventEditingDidEnd];
        [self.view addSubview:userTextField];
        
        UITextField *passwordTextField = [[UITextField alloc] initWithFrame:CGRectMake(30, 200, 375, 40)];
        passwordTextField.placeholder = @"请输入密码";
        userTextField.tag = 102;
        [passwordTextField addTarget:self action:@selector(textEditBegin:) forControlEvents:UIControlEventEditingDidBegin];
        [passwordTextField addTarget:self action:@selector(textEditEnd:) forControlEvents:UIControlEventEditingDidEnd];
        [self.view addSubview:passwordTextField];
    }
    
    - (void)textEditBegin:(UITextField *)textField
    {
        UILabel *label = [textField valueForKey:@"placeholderLabel"];
        label.textColor = [UIColor redColor];
    }
    
    - (void)textEditEnd:(UITextField *)textField
    {
        UILabel *label = [textField valueForKey:@"placeholderLabel"];
        label.textColor = [UIColor lightGrayColor];
    }
    

    3.2)、运用Runtime实现动图效果

    新建一个UITextField类别:
    
    UITextField+PlaceHoderColor.h
    
    分类内部实现:
    
    #import "UITextField+PlaceHoderColor.h"
    #import <objc/runtime.h>
    
    NSString * const placeholderColorName = @"placeholderColor";
    
    @implementation UITextField (PlaceHoderColor)
    
    // 需要给系统UITextField添加属性,只能使用runtime
    - (void)setPlaceholderColor:(UIColor *)placeholderColor
    {
        // 设置关联
        objc_setAssociatedObject(self,(__bridge const void *)(placeholderColorName), placeholderColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        // 设置占位文字颜色
        UILabel *placeholderLabel = [self valueForKeyPath:@"placeholderLabel"];
        placeholderLabel.textColor = placeholderColor;
    }
    
    //返回关联
    - (UIColor *)placeholderColor
    {
        return objc_getAssociatedObject(self, (__bridge const void *)(placeholderColorName));
    }
    
    + (void)load
    {
        //获取setPlaceholder
        Method  setPlaceholder = class_getInstanceMethod(self, @selector(setPlaceholder:));
        //获取bs_setPlaceholder
        Method  bs_setPlaceholder = class_getInstanceMethod(self, @selector(bs_setPlaceholder:));
        //交换方法
        method_exchangeImplementations(setPlaceholder, bs_setPlaceholder);
    }
    
    // 设置占位文字,并且设置占位文字颜色
    - (void)bs_setPlaceholder:(NSString *)placeholder
    {
        // 1.设置占位文字
        [self bs_setPlaceholder:placeholder];
        // 2.设置占位文字颜色
        self.placeholderColor = self.placeholderColor;
    }
    @end
    

    viewDidLoad于同3.1

    调用分类方法:
    - (void)textEditBegin:(UITextField *)textField
    {
        textField.placeholderColor = [UIColor redColor];
    }
    
    - (void)textEditEnd:(UITextField *)textField
    {
        textField.placeholderColor = [UIColor lightGrayColor];
    }
    

    相关文章

      网友评论

        本文标题:通过实例了解KVC 、 Runtime

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