第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 )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;
}

你可能感兴趣的:(第48周笔记总结(一))