偶遇runtime之关联、成员

我对runtime 并不精通,只是因为见的少而已,只是昨日 阿里百川的Demo和gitHub上下载项目遇见遇见过:

objc_setAssociatedObject(<#id object#>, <#const void *key#>, <#id value#>, <#objc_AssociationPolicy policy#>)和

objc_getAssociatedObject(<#id object#>, <#const void *key#>)  意在将“value”对象关联在“object”对象上,换句话说:“value”对象在“object”对象的整个生命周期中都是可用的。objc_setAssociatedObject(<#id object#>, <#const void *key#>, <#id value#>, <#objc_AssociationPolicy policy#>)参数中"object"为称为关联对象,"key"为成为关联关系的关键字、 唯一标示;"value"是被关联对象,"policy"为关联方针(是一枚举变量);那在什么情况下使用呢?

就目前所见:分类中@property声明的属性的setter、getter方法实现。我们知道在分类中使用@property声明的属性并不能正常的使用(在分类中@property不会生成_成员变量,也不会实现getter和setter方法,),那么我们只有自己手动实现了,可如下代码:

#import <Foundation/Foundation.h>

@interface NSObject (Sample)

@property (nonatomic,strong)NSString *kTitle;

@end

#import "NSObject+Sample.h"

#import <objc/runtime.h>

static const void *kKey = &kKey;

@implementation NSObject (Sample)

-(void)setKTitle:(NSString *)kTitle{

    objc_setAssociatedObject(self, kKey, kTitle, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

-(NSString *)kTitle{

    return objc_getAssociatedObject(self, kKey);

}

@end

这样我们就成功了为NSObject对象添加了一个属性(当然还缺个_kKey实例变量,但kTitle属性的点语法已经是可以正常使用了,可满足使用需求了)

在阿里百川多媒体的Demo里  是将一个方法里的临时变量关联到一个生命周期较长的对象上,以实现传值的功能。

当然,只要熟知其特性,在今后的编码中,只要有需求的,都是可以使用上的。

再来说一下runtime中 成员变量 Ivar:

先举个UIButton的例子

    UIButton * btn = [[UIButton alloc]init];

    unsigned int count = 0;

    Ivar *varList = class_copyIvarList([btn class], &count);

 NSMutableArray *tmplist = [NSMutableArray array];

    for (int i = 0 ; i < count; i++) {

        Ivar ivar = varList[i];

        const char *name = ivar_getName(ivar);

        NSString *propertyName = [[NSString alloc]initWithUTF8String:name];

        [tmplist addObject:propertyName];

  }

  NSLog(@"%@",tmplist);

这样你就可以在控制台上看到UIButton类声明过所有实例变量(一般前缀带有_)控制台输出如下:

  "_externalFlatEdge",

    "_contentLookup",

    "_contentEdgeInsets",

    "_titleEdgeInsets",

    "_imageEdgeInsets",

    "_backgroundView",

    "_floatingContentView",

    "_contentBackdropView",

    "_imageView",

    "_titleView",

    "_initialized",

    "_lastDrawingControlState",

    "_selectGestureRecognizer",

    "_buttonFlags",

    "_effectiveContentView",

    "_maskAnimationView",

    "_selectionView",

    "_lazyTitleViewFont",

    "_contentConstraints",

    "_internalTitlePaddingInsets"

以上的变量中,有许多都是私有的,我们无法直接使用setter、getter方法或是点语法来对其值进行存取的,只有使用kvc可以对私有属性的值进行存取:

  在上面代码的for循环中添加  id obj = [btn valueForKey:propertyName]; 执行 代码  却会导致程序崩溃。。。

  控制台给出的原因是'[<UIButton 0x7fd50257cec0> valueForUndefinedKey:]:

  回到上面列表中,细想来,便知:有些实例变量是通过属性声明生成的,有些是直接声明为_成员变量的,

  再看kvc 中  setValue:<#(nullable id)#> forKey:<#(nonnull NSString *)#>  与  valueForKey:<#(nonnull NSString *)#> 的方法存取实例的属性的值 是通过访问其setter、getter方法,如果仅仅只是声明为实例变量,没有setter、getter方法,则会调用 setValue:<#(nullable id)#> forUndefinedKey:<#(nonnull NSString *)#>与 valueForUndefinedKey:<#(nonnull NSString *)#> 方法,而这两个方法往往需要我们自己实现,一般而言我们对仅声明为_成员变量的没有多少需求,所以我们对forUndefinedKey 这两个方法的实现 一般内面没有什么代码,常见如下:

-(void)setValue:(id)value forUndefinedKey:(NSString *)key{ }

-(id)valueForUndefinedKey:(NSString *)key { return nil;}

--runtime 里面其实还有很多值得挖掘的知识;

  

posted on 2016-04-30 15:01  开发练习生  阅读(229)  评论(0编辑  收藏  举报