Source Code Learning - YYModel

源码看的是 YYKit,因为内容太多所以还是按照功能分开

kCFNull

NSNull 的用法 Mattt Thompson 说过是将空值存到数组或者字典

NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionary];
mutableDictionary[@"someKey"] = [NSNull null]; // Sets value of NSNull singleton for `someKey`
NSLog(@"Keys: %@", [mutableDictionary allKeys]); // @[@"someKey"]

kCFNull 这个是什么呢,看定义可以知道

typedef const struct CF_BRIDGED_TYPE(NSNull) __CFNull * CFNullRef;
const CFNullRef kCFNull;    // the singleton null instance

看个栗子

NSNull *null1 = (id)kCFNull;
NSNull *null2 = [NSNull null];

输出信息

(NSNull *) null1 = 0x0000000107b10af0
(NSNull *) null2 = 0x0000000107b10af0

说明 (id)kCFNull 就是 [NSNull null]

深拷贝

- (id)deepCopy {
    id obj = nil;
    @try {
        obj = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:self]];
    }
    @catch (NSException *exception) {
         NSLog(@"%@", exception);
    }
    return obj;
}

下面说说浅拷贝和深拷贝。集合类的拷贝分为浅拷贝和深拷贝

浅拷贝

NSArray *shallowCopyArray = [someArray copyWithZone:nil];

NSDictionary *shallowCopyDict = [[NSDictionary alloc] initWithDictionary:someDictionary copyItems:NO];

完全深拷贝

NSArray *deepCopyArray = [[NSArray alloc] initWithArray:someArray copyItems:YES];

NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
      [NSKeyedArchiver archivedDataWithRootObject:oldArray]];

这里的深拷贝是完全深拷贝(real-deep copy),如果使用 mutableCopy 是单层深拷贝 (one-level-deep copy)

NSMutableArray *array = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"one"],[NSMutableString stringWithString:@"two"],[NSMutableString stringWithString:@"three"],nil];

NSMutableArray *copyArray = [array mutableCopy];

NSMutableString *str = [array objectAtIndex:0];
[str appendString:@" One"];

NSLog(@"array:");
[self printArray:array];
NSLog(@"copyArray:");
[self printArray:copyArray];

[copyArray removeObjectAtIndex:0];

NSLog(@"array:");
[self printArray:array];
NSLog(@"copyArray:");
[self printArray:copyArray];

输出结果

array:
one One
two
three

copyArray:
one One
two
three

array:
one One
two
three

copyArray:
two
three

copyArray 通过 array 的 mutableCopy 方法进行了拷贝,该方法进行一层深拷贝,在内存中为新的数组对象分配了空间,并且将元素复制到另一个数组元素(仅将引用从一个数组元素复制到另一个数元素),这样的结果就是两个数组中的元素指向内存中的同一个字符。要实现完全深拷贝则要使用上面的完全深拷贝中的两种方式。

另外在使用 NSKeyedUnarchiver 时,如果解压数据为 nil 或者数据中有异常时,可能会导致崩溃,所以调用该方法时放到 try catch 中,保证程序不会崩溃。

Method Swizzling

Mattt Thompson 说 Method Swizzling 影响全局状态,所以降低资源竞争的可能性是非常重要的,因此 Method Swizzling 应当写到 load 方法中,并且应当只执行一次。好吧,看习惯 load 方法中调用的话,以为只能在初始化的时候使用,再看看 YYModel 就知道了,其实在哪里执行都可以,只是从效率的方面讲建议在 load 方法中执行。

- (void)viewDidLoad {
    // ...
    [YYTest swizzleClassMethod:@selector(printDZ) with:@selector(changePrintDZ)];

}

继续来讨论下这个黑魔法 swizzleClassMethod 的实现

+ (BOOL)swizzleClassMethod:(SEL)originalSel with:(SEL)newSel {
    Class class = object_getClass(self);
    Method originalMethod = class_getInstanceMethod(class, originalSel);
    Method newMethod = class_getInstanceMethod(class, newSel);
    if (!originalMethod || !newMethod) return NO;
    method_exchangeImplementations(originalMethod, newMethod);
    return YES;
}

这段代码大家都很熟悉了,但是需要注意的是,这是交换类方法,我们通常看到的 class_getInstanceMethod 的调用都是交换实例方法,为什么这个地方它也可用来交换类方法呢?答案只能在源码找了。看下 class_getClassMethod 的定义

Method class_getClassMethod(Class cls, SEL sel) {
    if (!cls  ||  !sel) return nil;
    return class_getInstanceMethod(cls->getMeta(), sel);
}

原来 class_getClassMethod 调用的就是 class_getInstanceMethod,只是参数传递的是 cls->getMeta()。

// NOT identical to this->ISA() when this is a metaclass
Class getMeta() {
    if (isMetaClass()) return (Class)this;
    else return this->ISA();
}

getMeta 返回的是 MetaClass,如果已经是 MetaClass 了直接返回本身,那么我们只要调用 class_getInstanceMethod 传递 metaClass,即 Class class = object_getClass(self) 作为参数,就等同于调用 class_getClassMethod 了。

参考文章

Nil NSNull NULL KCFNull
Copying Collections
NSKeyedUnarchiver crash

请我喝汽水儿