今天学习一下 KVC 深层次的东西 喜欢的可以进来看看,也许有你中意的哦~
1.成员变量&实例变量&属性
2.KVC初探
3.KVC赋值
4.KVC取值
5.KVC的异常处理
6.KVC的进阶用法
7.YYModel原理分析
8.category源码分析
1.成员变量&实例变量&属性 的区别是什么
//实例变量是一种特殊的成员变量
// class 类实例出来的变量 就是 实例变量 Btn 就是
@interface LGPerson : NSObject
{ //这个里面全部为成员变量
@public
NSString *Name;
UIButton *Btn ; //实例变量
id hello; //id 是一种特殊的class
}
// 这个是属性无疑 属性会有一个默认的 setter + getter 方法
@property (nonatomic, copy) NSString *nameX;
- 苹果早期的编译器是GCC 后面升级 变成 LLVM
- synthsize Name = _Name; //自动生成 setter 和 getter
- llvm之后 如果发现实例变量或者成员变量之后 没有匹配到实例变量的相应属性的时候 就会自动创建一个带 (_Name)
2.KVC初探
KVC 文档详情移步
先提几个问题
- KVC是什么 是一种 机制 -- 通过xx 间接访问成员变量
------------------------ -- 通过键值编码 - 有什么作用 进行一系列的键值编码
KVC使用
[self.textFiled setValue:[UIColor orangeColor] forKeyPath:@"_placeholderLabel.textColor"];
3.KVC赋值
当调用setValue:forKey: 代码时候,底层会
- 优先调用 setKey: 属性值 方法,代码通过setter 方法完成设置。 注意这里的key是指成员变量名,首字母大小写要符合KVC的命名规范, 下同
- 如果没有找到setName: 方法,KVC机制会检查 + (BOOL)accessInstanceVariablesDirectly 方法有没有返回YES,默认YES,如果你重写该方法为NO。KVC就直接执行setValue: forUndefineKey:方法,
不过一般不会这么做,所以KVC机制会搜索该类里面有没有名为_key的成员变量,无论是.h | .m 里面定义,也不论用什么样的访问修饰符,只要存在_key命名的变量,KVC都可以对该成员变量赋值。 - 如果该类没有setKey: 方法,也没有_key成员变量,KVC机制会搜索_isKey成员变量
- 继续没有就 继续搜索 _key 和 _isKey 成员变量,KVC也会继续搜索key 和 isKey 的成员变量,在给他们赋值。
- 如果上面列出的方法或者成员变量都不存在,系统将会执行该对象 setValue:forUndefinedKey:方法,默认是抛出异常。
4.KVC取值
当调用valueForKey的代码时候,
- 首先会按照 get
, , 或_ 顺序来查找 getter方法,找到这些方法就会直接调用,调用的过程遇到BOOL 或者Int 类型,会把他包装成NSNumber对象 - 如果以上四个方法没有找打 KVC就会查找 countOf
and objectIn AtIndex: 和 AtIndexes 格式的方法。如果找到countOf 和另外两个方法中的一个,那么就会返回一个可以响应NSArray所有方法的代理集合(他是NSKeyValueArray, 是NSArray的子类),并返回该对象 - 如果上面的方法没有找到,就会查找countOf
, enumeratorOf , 和 memberOf , 如果这个三个方法都找到就会返回一个NSSet 方法代理集合,这个代理集合会发NSSet的消息,就会以countOfKey,enumeratorOfKey,memberOfKey组合的形式调用。
简单的说就是如果你在自己的类定义了KVC的实现,并且实现了上面的方法,那么你可以将返回的对象当做数组(NSArray)/集合(NSSet)用了。
- 这个时候还没找到,就会检查类方法+(BOOL)accessInstanceVariablesDirectly,如果返回YES ,那么和之前的设值一样,会按_key,_isKey,key,isKey的顺序搜索成员变量名,这里不推荐这样做,因为这样直接访问实例变量破坏了封装性,使代码更脆弱。如果重写了+(BOOL)accessInstanceVariablesDirectly返回NO的话,那么直接调用valueForUndefinedKey:
- 最后再没找到就 调用 valueForUndefinedKey:
5.KVC的异常处理
几个比较常见的异常有
- 没有找到的一些值
[p setValue:nil forKey:@"subject"];
2.不存在的Key
[p setValue:@"hello" forKey:@"nickName"];
2.取值的时候不存在的Key
NSLog(@"%@",[p valueForKey:@"FKName"]);
需要对这些不安全的因素进行收集
// 对非对象类型赋值 不能设置空值
- (void)setNilValueForKey:(NSString *)key{
NSLog(@"%@的值不能为空",key);
}
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{
NSLog(@"不能对不存在的健赋值");
}
- (id)valueForUndefinedKey:(NSString *)key{
NSLog(@"不能对不存在的键取值");
return @"error";
}
- (BOOL)validateValue:(inout id _Nullable __autoreleasing *)ioValue forKey:(NSString *)inKey error:(out NSError * _Nullable __autoreleasing *)outError
{
if (*ioValue == nil || inKey == nil || inKey.length == 0) {
NSLog(@"value 可能为nil 或者key为nil或者空值");
return NO;
}
return YES;
}
6.KVC的进阶用法
- 先看用法
//控制器调用
-(void)arrayFKDemo
{
FKPerson *fp = FKPerson.new;
fp.mouseArr = [NSMutableArray arrayWithObjects:@"mouse0", @"mouse1", @"mouse2", @"mouse3", nil];
NSArray *arr = [fp valueForKey:@"mouse"]; // 动态成员变量
NSLog(@"mouse = %@", arr);
}
[20759:5051180] mouse = (
"mouse 0",
"mouse 1",
"mouse 2",
"mouse 3")
// 个数
- (NSUInteger)countOfMouse
{
return [self.mouseArr count];
}
//// 获取值
- (id)objectInMouseAtIndex:(NSUInteger)index
{
return [NSString stringWithFormat:@"mouse %lu", index];
}
// 是否包含这个成员对象
- (id)memberOfPens:(id)object {
return [self.penArr containsObject:object] ? object : nil;
}
// 迭代器
- (id)enumeratorOfPens {
// objectEnumerator
return [self.penArr reverseObjectEnumerator];
}
在上面person里面是没有mouse 这个key的,但是只要实现了 countOf
和 objectIn AtIndex: 这连个方法,程序就不会蹦,照常进行
如果是set集合 就调用 memberOf
和 countOf , enumeratorOf 其中一个方法 也是可以的 这里的用法可以根据官方文档来探索
7.YYModel原理分析
/// Returns the cached model class meta
+ (instancetype)metaWithClass:(Class)cls {
if (!cls) return nil;
static CFMutableDictionaryRef cache; //这里会声明一个字典,在下面做缓存使用,第一次进来缓存数据,
static dispatch_once_t onceToken;
static dispatch_semaphore_t lock;
dispatch_once(&onceToken, ^{
cache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
lock = dispatch_semaphore_create(1);
});
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
//第二次进来就会直接从全局的cache缓存里面去,性能高
_YYModelMeta *meta = CFDictionaryGetValue(cache, (__bridge const void *)(cls));
dispatch_semaphore_signal(lock);
if (!meta || meta->_classInfo.needUpdate) {
meta = [[_YYModelMeta alloc] initWithClass:cls];
if (meta) {
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
CFDictionarySetValue(cache, (__bridge const void *)(cls), (__bridge const void *)(meta));
dispatch_semaphore_signal(lock);
}
}
return meta;
}
- initWithClass 方法里面 先进去判断 黑白名单过滤操作 ,然后下一步会进行属性替换操作 (modelContainerPropertyGenericClass) 并进行处理(按照不同的类型处理)
+ (NSDictionary *)modelContainerPropertyGenericClass{
return @{@"books" : FKSubModel.class,
@"infoDict" : FKSubTModel.class,
@"likedUserIds" : NSNumber.class
};
}
方法进行到这一步之后会将里面各种不同的数据类型进行转换
+ (NSDictionary *)_yy_dictionaryWithJSON:(id)json {
if (!json || json == (id)kCFNull) return nil;
NSDictionary *dic = nil;
NSData *jsonData = nil;
if ([json isKindOfClass:[NSDictionary class]]) {
dic = json;
} else if ([json isKindOfClass:[NSString class]]) {
jsonData = [(NSString *)json dataUsingEncoding : NSUTF8StringEncoding];
} else if ([json isKindOfClass:[NSData class]]) {
jsonData = json;
}
if (jsonData) {
dic = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL];
if (![dic isKindOfClass:[NSDictionary class]]) dic = nil;
}
return dic;
}
转换完了之后,就会得到一个jsonData并序列化,结果就会返回一个dic
- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic {
if (!dic || dic == (id)kCFNull) return NO;
if (![dic isKindOfClass:[NSDictionary class]]) return NO;
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:object_getClass(self)];
if (modelMeta->_keyMappedCount == 0) return NO;
// 这里会拿到当前meta里面所有的map所有的类,发送一个响应的方法CFArrayApplyFunction
if (modelMeta->_hasCustomWillTransformFromDictionary) {
dic = [((id)self) modelCustomWillTransformFromDictionary:dic];
if (![dic isKindOfClass:[NSDictionary class]]) return NO;
}
ModelSetContext context = {0};
context.modelMeta = (__bridge void *)(modelMeta);
context.model = (__bridge void *)(self);
context.dictionary = (__bridge void *)(dic);
//这个里面都会调用ModelSetWithPropertyMetaArrayFunction方法
if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) {
CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);
if (modelMeta->_keyPathPropertyMetas) {
CFArrayApplyFunction((CFArrayRef)modelMeta->_keyPathPropertyMetas,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_keyPathPropertyMetas)),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
if (modelMeta->_multiKeysPropertyMetas) {
CFArrayApplyFunction((CFArrayRef)modelMeta->_multiKeysPropertyMetas,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_multiKeysPropertyMetas)),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
} else {
CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas,
CFRangeMake(0, modelMeta->_keyMappedCount),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
if (modelMeta->_hasCustomTransformFromDictionary) {
return [((id)self) modelCustomTransformFromDictionary:dic];
}
return YES;
}
/**
Apply function for model property meta, to set dictionary to model.
@param _propertyMeta should not be nil, _YYModelPropertyMeta.
@param _context _context.model and _context.dictionary should not be nil.
*/
static void ModelSetWithPropertyMetaArrayFunction(const void *_propertyMeta, void *_context) {
ModelSetContext *context = _context;
__unsafe_unretained NSDictionary *dictionary = (__bridge NSDictionary *)(context->dictionary);
__unsafe_unretained _YYModelPropertyMeta *propertyMeta = (__bridge _YYModelPropertyMeta *)(_propertyMeta);
if (!propertyMeta->_setter) return;
id value = nil;
//判断是否为array类型的
if (propertyMeta->_mappedToKeyArray) {
value = YYValueForMultiKeys(dictionary, propertyMeta->_mappedToKeyArray);
} else if (propertyMeta->_mappedToKeyPath) {
//判断是否为KeyPath类型的
value = YYValueForKeyPath(dictionary, propertyMeta->_mappedToKeyPath);
} else {
//这里是直接转换值
value = [dictionary objectForKey:propertyMeta->_mappedToKey];
}
if (value) {
__unsafe_unretained id model = (__bridge id)(context->model);
ModelSetValueForProperty(model, value, propertyMeta);
}
}
/// Get the value with multi key (or key path) from dictionary
/// The dic should be NSDictionary
static force_inline id YYValueForMultiKeys(__unsafe_unretained NSDictionary *dic, __unsafe_unretained NSArray *multiKeys) {
id value = nil;
// for遍历里面所有的key拿出来 相应的进行下次路由 这里分string类型 和 其他类型,其他类型继续进行路由
for (NSString *key in multiKeys) {
if ([key isKindOfClass:[NSString class]]) {
value = dic[key];
if (value) break;
} else {
// 路由这里就要进行遍历,将嵌套的一层一层全部拿出来
value = YYValueForKeyPath(dic, (NSArray *)key);
if (value) break;
}
}
return value;
}
//最后走 ModelSetValueForProperty 方法 ,NSNumber NSString NSAttributedString各种各样的类型在里面都要发送msg_send 调用 meta->_setter 方法,对 (也就是发送setter 信息)
/**
Set value to model with a property meta.
@discussion Caller should hold strong reference to the parameters before this function returns.
@param model Should not be nil.
@param value Should not be nil, but can be NSNull.
@param meta Should not be nil, and meta->_setter should not be nil.
*/
static void ModelSetValueForProperty(__unsafe_unretained id model,
__unsafe_unretained id value,
__unsafe_unretained _YYModelPropertyMeta *meta) ;
8. category源码分析
如果给一个类添加一个分类,并重写这个类的某一个方法,问,到底会不会覆盖类里面的方法
// 遍历方法 可以通过这个方法来验证是否覆盖
-(void) printMethodNameOfClass:(Class)cls
{
unsigned int count;
//获取方法数组
Method *methodList = class_copyMethodList(cls, &count);
//存储方法名
NSMutableString *methodNames = [NSMutableString string];
//遍历所有方法
for (int i = 0 ; i
- 分类的方法是如何加载
/***********************************************************************
* _objc_init
* Bootstrap initialization. Registers our image notifier with dyld.
* Called by libSystem BEFORE library initialization time
**********************************************************************/
//这里就是所有的img进行的镜像加载
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
environ_init();
tls_init();
static_init();
lock_init();
exception_init();
// 通过map_images 这个函数
//进入到 void map_images_nolock(unsigned mhCount, const char * const mhPaths[],const struct mach_header * const mhdrs[])
// 开始读所有的镜像文件
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
//读取函数
if (hCount > 0) {
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
//此处省略N多代码
// 这里就会出来很多的类,包括子类等等都会加载过来
for (i = 0; i < count; i++) {
Class cls = (Class)classlist[I];
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
//类加载
if (newCls != cls && newCls) {
// Class was moved but not deleted. Currently this occurs
// only when the new class resolved a future class.
// Non-lazily realize the class below.
resolvedFutureClasses = (Class *)
realloc(resolvedFutureClasses,
(resolvedFutureClassCount+1) * sizeof(Class));
resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
}
}
//方法编号的加载
static size_t UnfixedSelectors;
{
mutex_locker_t lock(selLock);
for (EACH_HEADER) {
if (hi->isPreoptimized()) continue;
bool isBundle = hi->isBundle();
SEL *sels = _getObjc2SelectorRefs(hi, &count);
UnfixedSelectors += count;
for (i = 0; i < count; i++) {
const char *name = sel_cname(sels[i]);
sels[i] = sel_registerNameNoLock(name, isBundle);
}
}
}
// Discover protocols. Fix up protocol refs.
//协议加载
for (EACH_HEADER) {
extern objc_class OBJC_CLASS_$_Protocol;
Class cls = (Class)&OBJC_CLASS_$_Protocol;
assert(cls);
NXMapTable *protocol_map = protocols();
bool isPreoptimized = hi->isPreoptimized();
bool isBundle = hi->isBundle();
// 最后其实就是一个列表 通过这个_getObjc2ProtocolList函数
// 整个进程会维护这张表
protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map,
isPreoptimized, isBundle);
}
}
// Discover categories.
// 探索重点~~~!!!
// category 加载 他也是通过这个_getObjc2CategoryList来加载的
for (EACH_HEADER) {
category_t **catlist =
_getObjc2CategoryList(hi, &count);
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
for (i = 0; i < count; i++) {
category_t *cat = catlist[I];
Class cls = remapClass(cat->cls);
if (!cls) {
// Category's target class is missing (probably weak-linked).
// Disavow any knowledge of this category.
catlist[i] = nil;
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
"missing weak-linked target class",
cat->name, cat);
}
continue;
}
// Process this category.
// First, register the category with its target class.
// Then, rebuild the class's method lists (etc) if
// the class is realized.
bool classExists = NO;
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
addUnattachedCategoryForClass(cat, cls, hi);
if (cls->isRealized()) {
remethodizeClass(cls);
classExists = YES;
}
if (PrintConnecting) {
_objc_inform("CLASS: found category -%s(%s) %s",
cls->nameForLogging(), cat->name,
classExists ? "on existing class" : "");
}
}
if (cat->classMethods || cat->protocols
|| (hasClassProperties && cat->_classProperties))
{
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
if (cls->ISA()->isRealized())
{
// remethodizeClass 函数加载
remethodizeClass(cls->ISA());
}
if (PrintConnecting) {
_objc_inform("CLASS: found category +%s(%s)",
cls->nameForLogging(), cat->name);
}
}
}
}
}
static void remethodizeClass(Class cls)
{
category_list *cats;
bool isMeta;
runtimeLock.assertLocked();
isMeta = cls->isMetaClass();
// Re-methodizing: check for more categories
//判断是否能找到这个category
if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {
if (PrintConnecting) {
_objc_inform("CLASS: attaching categories to class '%s' %s",
cls->nameForLogging(), isMeta ? "(meta)" : "");
}
//找到就attachCategories 把cats贴上去
attachCategories(cls, cats, true /*flush caches*/);
free(cats);
}
}
// 通过- 汇编lldb - 源码 - 官方文档 - 坑
一级标题
二级标题
五级标题
- 列表第一项
- 列表第二项
- 有序列表第一项
- 有序列表第二项
[图片上传失败...(image-12bdd7-1557046383947)]
斜体
粗体
引用段落
代码块