一屎鳍、CFMutableDictionaryRef的使用
最近在看YYModel的源碼蝎抽,發(fā)現(xiàn)其中多次使用了CFMutableDictionaryRef來對類相關(guān)信息進行緩存斟赚。為此舍肠,對CFMutableDictionaryRef進行了一番探討侦讨。
1. CFMutableDictionaryRef的介紹
CFMutableDictionaryRef是Core-Foundation框架下的一個集合,它提供C語言接口儒士,與我們常用的Foundation框架的NSMutableDictionary類似的止,只不過我們使用的NSMutableDictionary的接口對應(yīng)是OC接口。
2.CFMutableDictionaryRef的使用
CFMutableDictionaryRef的使用和我們常用的NSMutableDictionary是非常相似的着撩,唯一要注意的一點就是由于CFMutableDictionaryRef使用的C語言接口诅福,因此我們需要將對象類型進行橋接,轉(zhuǎn)換為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
兩者都是字典的實現(xiàn)類氓润,只不過兩者是使用的接口不同,有一個比較重要的區(qū)別是NSMutableDictionary要求key必須實現(xiàn)了NSCopying協(xié)議薯鳍,而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的應(yīng)用場景
CFMutableDictionaryRef使用比較多的場景是作為cache緩存一些沒有實現(xiàn)NSCopying協(xié)議的key,比如在YYModel中,作者是使用Class為key進行緩存采章,顯然Class對象并沒有實現(xiàn)NSCopying協(xié)議运嗜。
+ (instancetype)metaWithClass:(Class)cls {
if (!cls) return nil;
//定義相關(guān)變量
static CFMutableDictionaryRef cache;
static dispatch_once_t onceToken;
static dispatch_semaphore_t lock;
dispatch_once(&onceToken, ^{
//創(chuàng)建緩存
cache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
lock = dispatch_semaphore_create(1); //創(chuàng)建鎖
});
//從緩存中獲取內(nèi)容
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); //開啟鎖
_YYModelMeta *meta = CFDictionaryGetValue(cache, (__bridge const void *)(cls)); //從緩存中獲取
dispatch_semaphore_signal(lock); //關(guān)閉鎖
//更新緩存內(nèi)容
if (!meta || meta->_classInfo.needUpdate) { //若緩存中不存在或類相關(guān)信息需要更新
meta = [[_YYModelMeta alloc] initWithClass:cls]; //重新獲取
if (meta) {
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
CFDictionarySetValue(cache, (__bridge const void *)(cls), (__bridge const void *)(meta)); //設(shè)置緩存
dispatch_semaphore_signal(lock);
}
}
return meta;
}
二、class類型的判斷
我們常用的class類型判斷的方法有三種:isMemberOfClass悯舟、isKindOfClass和isSubclassOfClass.接下來我們使用三個不同的類担租,來查看三種不同方法的區(qū)別.
@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]
根據(jù)上面的輸出,1-3行可以判斷:
isMemberOfClass是對象實例的判斷方法抵怎,它只有在對象為當(dāng)前類的實例時奋救,才返回true.
由4-6行,可以判斷:
isKindOfClass是對象實例的判斷方法反惕,它在對象為當(dāng)前類或子類的實例時尝艘,都會返回ture.
由7-9行,可以判斷:
isSubclassOfClass是對象類的判斷方法姿染,是一個類方法背亥,它在類為當(dāng)前類或子類時,返回true.
總結(jié):
我們比較常用的是isKindOfClass方法悬赏,用于判斷是否為當(dāng)前的class或class的子類.
對于isMemberOfClass方法狡汉,是用于判斷是否為當(dāng)前的class,相當(dāng)于更為嚴格的isKindOfClass方法.
而isSubclassOfClass是一個類方法闽颇,通常用于類之間判斷是否存在繼承關(guān)系.
屬性(property)和成員變量(ivar)之間的區(qū)別
比較直接理解的方法可以看做:property = ivar + setter + getter.通常來說盾戴,我們都是用屬性去定義變量,當(dāng)然對于一些私有變量兵多,如果需要考慮性能尖啡,我們可以使用ivar來避免setter和getter帶來的性能消耗.
Core-Foundation集合的遍歷
在Core-Foundation集合中我們比較常用的是CFDictionary和CFArray,這就涉及到集合的遍歷問題剩膘,CF集合的遍歷方式與NS集合的遍歷方式有很大的區(qū)別.
CFDictionaryRef的遍歷
CFDictionaryRef的遍歷原型:
void CFDictionaryApplyFunction(CFDictionaryRef theDict, CFDictionaryApplierFunction CF_NOESCAPE applier, void *context);
這里比較重要的是第二個參數(shù)衅斩,是一個需要定義的遍歷函數(shù):
typedef void (*CFDictionaryApplierFunction)(const void *key, const void *value, void *context);
第三個參數(shù)是額外的參數(shù),如果需要傳入援雇,需要通過__bridge轉(zhuǎn)換為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);
}
輸出結(jié)果:
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遍歷函數(shù)原型:
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;
}