第48周笔记总结(一)
一、CFMutableDictionaryRef的使用
最近在看YYModel的源码,发现其中多次使用了CFMutableDictionaryRef来对类相关信息进行缓存。为此,对CFMutableDictionaryRef进行了一番探讨。
1. CFMutableDictionaryRef的介绍
CFMutableDictionaryRef是Core-Foundation框架下的一个集合,它提供C语言接口,与我们常用的Foundation框架的NSMutableDictionary类似,只不过我们使用的NSMutableDictionary的接口对应是OC接口。
2.CFMutableDictionaryRef的使用
CFMutableDictionaryRef的使用和我们常用的NSMutableDictionary是非常相似的,唯一要注意的一点就是由于CFMutableDictionaryRef使用的C语言接口,因此我们需要将对象类型进行桥接,转换为C类型。
CFMutableDictionaryRef myDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
NSString *key = @"someKey";
NSNumber *value = [NSNumber numberWithInt: 1];
//set
CFDictionarySetValue(myDict, (__bridge void *)key, (__bridge void *)value);
//get
id dictValue = (__bridge id)CFDictionaryGetValue(myDict, (__bridge void *)key);
//remove
CFDictionaryRemoveValue(myDict, (__bridge void *)key);
3. NSMutableDictionary vs CFMutableDictionaryRef
两者都是字典的实现类,只不过两者是使用的接口不同,有一个比较重要的区别是NSMutableDictionary要求key必须实现了NSCopying协议,而CFMutableDictionaryRef是没有这个要求的。
//NSMutableDictionary
- (void)setObject:(id)anObject forKey:(id <NSCopying>)aKey;
//CFMutableDictionaryRef
CF_EXPORT
void CFDictionarySetValue(CFMutableDictionaryRef theDict, const void *key, const void *value);
4.CFMutableDictionaryRef的应用场景
CFMutableDictionaryRef使用比较多的场景是作为cache缓存一些没有实现NSCopying协议的key,比如在YYModel中,作者是使用Class为key进行缓存,显然Class对象并没有实现NSCopying协议。
+ (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); //开启锁
_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;
}
二、class类型的判断
我们常用的class类型判断的方法有三种:isMemberOfClass、isKindOfClass和isSubclassOfClass.接下来我们使用三个不同的类,来查看三种不同方法的区别.
@interface RootItem : NSObject
@end
@implementation RootItem
@end
@interface SubItem : RootItem
@end
@implementation SubItem
@end
@implementation ClassDemo
- (void)classTypeTest {
NSObject *obj = [NSObject new];
RootItem *root = [RootItem new];
SubItem *sub = [SubItem new];
if ([obj isMemberOfClass:[RootItem class]]) {
NSLog(@"obj isMemberOfClass [RootIem Class]");
} else {
NSLog(@"obj isNotMemberOfClass [RootIem Class]");
}
if ([root isMemberOfClass:[RootItem class]]) {
NSLog(@"root isMemberOfClass [RootItem Class]");
} else {
NSLog(@"root isNotMemberOfClass [RootItem Class]");
}
if ([sub isMemberOfClass:[RootItem class]]) {
NSLog(@"sub isMemberOfClass [RootItem Class]");
} else {
NSLog(@"sub isNotMemberOfClass [RootItem Class]");
}
if ([obj isKindOfClass:[RootItem class]]) {
NSLog(@"obj isKindOfClass [RootIem Class]");
} else {
NSLog(@"obj isNotKindOfClass [RootIem Class]");
}
if ([root isKindOfClass:[RootItem class]]) {
NSLog(@"root isKindOfClass [RootIem Class]");
} else {
NSLog(@"root isNotKindOfClass [RootIem Class]");
}
if ([sub isKindOfClass:[RootItem class]]) {
NSLog(@"sub isKindOfClass [RootIem Class]");
} else {
NSLog(@"sub isNotKindOfClass [RootIem Class]");
}
if ([NSObject isSubclassOfClass:[RootItem class]]) {
NSLog(@"NSObject isSubclassOfClass [RootIem Class]");
} else {
NSLog(@"NSObject isNotSubclassOfClass [RootIem Class]");
}
if ([RootItem isSubclassOfClass:[RootItem class]]) {
NSLog(@"RooItem isSubclassOfClass [RootIem Class]");
} else {
NSLog(@"RooItem isNotSubclassOfClass [RootIem Class]");
}
if ([SubItem isSubclassOfClass:[RootItem class]]) {
NSLog(@"SubItem isSubclassOfClass [RootIem Class]");
} else {
NSLog(@"SubItem isNotSubclassOfClass [RootIem Class]");
}
}
@end
输出:
2018-12-01 16:55:38.599310+0800 Note[5434:3102432] obj isNotMemberOfClass [RootIem Class]
2018-12-01 16:55:38.599434+0800 Note[5434:3102432] root isMemberOfClass [RootItem Class]
2018-12-01 16:55:38.599500+0800 Note[5434:3102432] sub isNotMemberOfClass [RootItem Class]
2018-12-01 16:55:38.599556+0800 Note[5434:3102432] obj isNotKindOfClass [RootIem Class]
2018-12-01 16:55:38.599627+0800 Note[5434:3102432] root isKindOfClass [RootIem Class]
2018-12-01 16:55:38.599713+0800 Note[5434:3102432] sub isKindOfClass [RootIem Class]
2018-12-01 16:55:38.599787+0800 Note[5434:3102432] NSObject isNotSubclassOfClass [RootIem Class]
2018-12-01 16:55:38.599860+0800 Note[5434:3102432] RooItem isSubclassOfClass [RootIem Class]
2018-12-01 16:55:38.599930+0800 Note[5434:3102432] SubItem isSubclassOfClass [RootIem Class]
根据上面的输出,1-3行可以判断:
isMemberOfClass是对象实例的判断方法,它只有在对象为当前类的实例时,才返回true.
由4-6行,可以判断:
isKindOfClass是对象实例的判断方法,它在对象为当前类或子类的实例时,都会返回ture.
由7-9行,可以判断:
isSubclassOfClass是对象类的判断方法,是一个类方法,它在类为当前类或子类时,返回true.
总结:
我们比较常用的是isKindOfClass方法,用于判断是否为当前的class或class的子类.
对于isMemberOfClass方法,是用于判断是否为当前的class,相当于更为严格的isKindOfClass方法.
而isSubclassOfClass是一个类方法,通常用于类之间判断是否存在继承关系.
属性(property)和成员变量(ivar)之间的区别
比较直接理解的方法可以看做:property = ivar + setter + getter.通常来说,我们都是用属性去定义变量,当然对于一些私有变量,如果需要考虑性能,我们可以使用ivar来避免setter和getter带来的性能消耗.
Core-Foundation集合的遍历
在Core-Foundation集合中我们比较常用的是CFDictionary和CFArray,这就涉及到集合的遍历问题,CF集合的遍历方式与NS集合的遍历方式有很大的区别.
CFDictionaryRef的遍历
CFDictionaryRef的遍历原型:
void CFDictionaryApplyFunction(CFDictionaryRef theDict, CFDictionaryApplierFunction CF_NOESCAPE applier, void *context);
这里比较重要的是第二个参数,是一个需要定义的遍历函数:
typedef void (*CFDictionaryApplierFunction)(const void *key, const void *value, void *context);
第三个参数是额外的参数,如果需要传入,需要通过__bridge转换为void *类型
@interface CFItem : NSObject
@property (nonatomic, copy) NSString *itemname;
@property (nonatomic, assign) NSInteger itemnum;
+ (instancetype)createCFItemWithname:(NSString *)name num:(NSInteger)num;
@end
@implementation CFItem
+ (instancetype)createCFItemWithname:(NSString *)name num:(NSInteger)num {
CFItem *item = [CFItem new];
item.itemname = name;
item.itemnum = num;
return item;
}
@end
static void printDict (const void *_key, const void *_value, void *context) {
NSString *key = (__bridge NSString *)_key;
CFItem *value = (__bridge CFItem *)_value;
NSLog(@"dict[%@] = %@", key, @{@"name":value.itemname, @"num":@(value.itemnum)});
}
+ (void)cfDictTest {
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(dict, (__bridge void *)(@"key1"), (__bridge void *)([CFItem createCFItemWithname:@"item1" num:1]));
CFDictionarySetValue(dict, (__bridge void *)(@"key2"), (__bridge void *)([CFItem createCFItemWithname:@"item2" num:2]));
CFDictionarySetValue(dict, (__bridge void *)(@"key3"), (__bridge void *)([CFItem createCFItemWithname:@"item3" num:3]));
CFDictionaryApplyFunction(dict, printDict, NULL);
}
输出结果:
2018-12-09 12:17:51.758464+0800 Note[37238:7172086] dict[key1] = {
name = item1;
num = 1;
}
2018-12-09 12:17:51.758612+0800 Note[37238:7172086] dict[key3] = {
name = item3;
num = 3;
}
2018-12-09 12:17:51.758740+0800 Note[37238:7172086] dict[key2] = {
name = item2;
num = 2;
}
CFArrayRef的遍历
CFArrayRef遍历函数原型:
CF_EXPORT
void CFArrayApplyFunction(CFArrayRef theArray, CFRange range, CFArrayApplierFunction CF_NOESCAPE applier, void *context);
typedef void (*CFArrayApplierFunction)(const void *value, void *context);
static void printArr (const void *_value, void *context) {
CFItem *value = (__bridge CFItem *)_value;
NSLog(@"CFItem = %@", @{@"name":value.itemname, @"num":@(value.itemnum)});
}
+ (void)cfArrTest {
CFMutableArrayRef arr = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
CFArrayAppendValue(arr, (__bridge void *)([CFItem createCFItemWithname:@"item1" num:1]));
CFArrayAppendValue(arr, (__bridge void *)([CFItem createCFItemWithname:@"item2" num:2]));
CFArrayAppendValue(arr, (__bridge void *)([CFItem createCFItemWithname:@"item3" num:3]));
CFArrayApplyFunction(arr, CFRangeMake(0, CFArrayGetCount(arr)), printArr, NULL);
}
输出:
2018-12-09 12:17:51.758871+0800 Note[37238:7172086] CFItem = {
name = item1;
num = 1;
}
2018-12-09 12:17:51.758981+0800 Note[37238:7172086] CFItem = {
name = item2;
num = 2;
}
2018-12-09 12:17:51.759112+0800 Note[37238:7172086] CFItem = {
name = item3;
num = 3;
}