我对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 里面其实还有很多值得挖掘的知识;