010-YapDatabase 食用指南

2017-11-26  本文已影响164人  Yasic

YapDatabase 是一个工作在 iOS 和 MAC 上的数据库,有两大主要特性:

同时还有以下特性

并发行

YapDatabase 中的只读连接会保存数据库的即时快照,即使其他连接改变数据,也不会影响当前的连接。但必须遵循以下规则:

存储

YapDatabase 支持任何类型的 object,只要设置好序列化和反序列化流程就可以,YapDatabase 有提供默认的序列化和反序列化流程,当然也支持自定义。对于支持了 NSCoding 协议的类,可以不需要额外的设置,比如 Cocoa 中的大多数内置类

对于自定义类,只需要实现 NSCoding 的序列化、反序列化方法就可以了。

@interface Person : NSObject<NSCoding>

@property(strong, nonatomic) NSString *name;
@property(strong, nonatomic) NSString *gender;
@property(assign, nonatomic) NSInteger age;

@end

@implementation Person

- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
    if ([super init])
    {
        self.name = [aDecoder decodeObjectForKey:@"name"];
        self.gender = [aDecoder decodeObjectForKey:@"gender"];
        self.age = [aDecoder decodeIntegerForKey:@"age"];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:self.name forKey:@"name"];
    [aCoder encodeObject:self.gender forKey:@"gender"];
    [aCoder encodeInteger:self.age forKey:@"age"];
}

@end

如果序列化类的某个属性也是支持 NSCoding 的类,则也可以直接存进数据库,同样的,对于一个数组,序列化和反序列化信息也会依次发送给每一个成员。

YapDatabase 包含一些开箱即用的序列化函数

/**
默认的序列化/反序列化函数,实现了 NSCoding 协议,任何支持 NSCoding 协议的对象(包括系统自带的大多数类)都可以被序列化/反序列化。
**/
+ (YapDatabaseSerializer)defaultSerializer;
+ (YapDatabaseDeserializer)defaultDeserializer;

/**
属性列表序列化/反序列化函数,只支持 SData, NSString, NSArray, NSDictionary, NSDate, NSNumber 这些类,采取了一些优化措施
**/
+ (YapDatabaseSerializer)propertyListSerializer;
+ (YapDatabaseDeserializer)propertyListDeserializer;

/**
一个针对 NSDate 对象的快速序列化/反序列化函数
**/
+ (YapDatabaseSerializer)timestampSerializer;
+ (YapDatabaseDeserializer)timestampDeserializer;

自定义序列化/反序列化函数的方法可以用于加密、压缩和性能优化等方面。

typedef NSData* (^YapDatabaseSerializer)(NSString *collection, NSString *key, id object);
typedef id (^YapDatabaseDeserializer)(NSString *collection, NSString *key, NSData *data);

集合

集合为众多拥有同一关键属性的元素提供了更方拜年的存储结构,它为元素提供除了 key 值外另一个层次的标识,所以 YapDatabase 也可以理解为是 “字典的字典”。

YapDatabase 关于集合的 API 有以下几个

/**
 * Returns the total number of collections.
 * Each collection may have 1 or more key/object pairs.
**/
- (NSUInteger)numberOfCollections;

/**
 * Returns the total number of keys in the given collection.
 * Returns zero if the collection doesn't exist (or all key/object pairs from the collection have been removed).
**/
- (NSUInteger)numberOfKeysInCollection:(NSString *)collection;

/**
 * Object access.
 * Objects are automatically deserialized using database's configured deserializer.
**/
- (id)objectForKey:(NSString *)key inCollection:(NSString *)collection;

/**
 * Fast enumeration over all keys in the given collection.
 *
 * This uses a "SELECT key FROM database WHERE collection = ?" operation,
 * and then steps over the results invoking the given block handler.
**/
- (void)enumerateKeysInCollection:(NSString *)collection
                       usingBlock:(void (^)(NSString *key, BOOL *stop))block;

可以看到一个 object 在 YapDatabase 中是依靠 collection 和 key 两个标识共同唯一确定的。

缓存

YapDatabase 的每一个连接都有自己专属的数据库缓存,与 sqlite 的二进制数据缓存层相比,YapDatabase 的缓存层直接缓存 objectivec 对象,减少了序列化和反序列化的开销。

缓存默认打开,大小为 250,以下是 API

// 这一属性可以选择关闭或开启缓存
@property (atomic, assign, readwrite) BOOL objectCacheEnabled;
@property (atomic, assign, readwrite) BOOL metadataCacheEnabled;

// 可以设置缓存大小,如果赋值为 0 则缓存空间无限
@property (atomic, assign, readwrite) NSUInteger objectCacheLimit;
@property (atomic, assign, readwrite) NSUInteger metadataCacheLimit;

支持对象与元数据分开缓存,也支持在运行中进行缓存大小的修改,每一个连接的缓存都会自动与数据库进行同步。

元数据 Metedata

YapDatabase 支持存储的元祖不仅仅包含对象,还有元数据,底层的数据库表也为元数据开辟了独立的存储列。对象是必须有的,但是元数据是可选的,如果对象为 nil 则元组会被移除,但是元数据为 nil 并不会。元数据与对象可以拥有独立的缓存机制和序列化/反序列化函数。

元数据相关的存储与更新 API 有以下这些

/**
 * Invokes setObject:forKey:inCollection:withMetadata:,
 * and passes a nil value for the metadata parameter.
**/
- (void)setObject:(nullable id)object
           forKey:(NSString *)key
     inCollection:(nullable NSString *)collection;

/**
 * If you call this method with a nil object, then it will delete the row.
 * (Equivalent to calling removeObjectForKey:inCollection:)
 * 
 * Otherwise, this method inserts/updates the row,
 * and sets BOTH the object & metadata columns to the given values.
**/
- (void)setObject:(nullable id)object
           forKey:(NSString *)key
     inCollection:(nullable NSString *)collection
     withMetadata:(nullable id)metadata;

/**
 * If a row with the given collection/key already exists,
 * then this method updates ONLY the object value.
 * The metadata value for the row isn't touched. (It remains whatever it was before.)
 * 
 * Again, it's not possible to have a nil object for a row.
 * So if you try to set the object to nil, this is just going to delete the row.
**/
- (void)replaceObject:(nullable id)object
               forKey:(NSString *)key
         inCollection:(nullable NSString *)collection;

/**
 * If a row with the given collection/key already exists,
 * then this method updates ONLY the metadata value.
 * The object value for the row isn't touched. (It remains whatever it was before.)
**/
- (void)replaceMetadata:(nullable id)metadata
                 forKey:(NSString *)key
           inCollection:(nullable NSString *)collection;

最佳实践

相关 API 与操作

1. 删除

2. 获取总数

3. 获取列表

4. 获取对象与元数据

5. 获取原始数据

下面的方法会跳过 oc 层缓存,直接获取数据库中的原始数据,因此速度不如上面的方法。

- (BOOL)getSerializedObject:(NSData * __nullable * __nullable)serializedObjectPtr
        serializedMetadata:(NSData * __nullable * __nullable)serializedMetadataPtr
                    forKey:(NSString *)key
              inCollection:(nullable NSString *)collection;

6. 枚举

7. 存储和更新

这里要注意如果存储的对象所属 key 是已经存储在数据库的,则会自动更新这个元素,如果传递的对象是 nil,则会移除这个元素。

上一篇下一篇

猜你喜欢

热点阅读