源码看的是 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