美文网首页
010-YapDatabase 食用指南

010-YapDatabase 食用指南

作者: Yasic | 来源:发表于2017-11-26 19:21 被阅读164次

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

  • 基于 sqlite 建立的 collection/key/value/metadata 基础存储功能
  • 提供类似视图、辅助索引、全文搜索等高级功能的插件

同时还有以下特性

  • 并发性:可以同时在多个连接上进行读写操作,不会阻塞主线程
  • 内建缓存:相比于 sqlite 的原始字节缓存,YapDatabase 的缓存可以跳过序列化和反序列化阶段
  • 集合:支持集合存储
  • 元数据 metadata:支持元数据存储,可以存储一些 object 的额外信息,比如时间戳等
  • 性能:在主线程获取数千对象不会掉帧
  • objectivec API
  • 拓展:内置拓展架构,同时支持自定义
  • 视图:YapDatabase 内置的 view 使得过滤、组合和排序数据非常便捷
  • 辅助索引:通过索引重要属性来加快搜索速度
  • 全文搜索:基于 sqlite 的 FTS 模块,可以以最小代价获得极速的搜索

并发行

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

  • 可以同时建立多个连接 Connection
  • 每一个连接都是线程安全的
  • 可以同时拥有多个只读事务而不阻塞
  • 可以同时拥有多个只读事务和一个读写事务而不阻塞
  • 对于每一个数据库,同一时间只能有一个读写事务,有唯一一个串行duilie执行读写事务
  • 对于每一个连接,同一时间只能有一个事务,每一个连接都维护一个串行队列执行事务

存储

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

  • NSString
  • NSNumber
  • NSArray
  • NSDictionary
  • NSSet
  • NSData
  • UIColor
  • UIImage

对于自定义类,只需要实现 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;

最佳实践

  • 避免用同一个连接在主线程和其他线程同时执行事务,因为连接只能串行执行事务
  • 避免创建太多连接,会带来开销问题,同时会降低缓存命中,因为连接中执行的事务少,缓存性能没有表现出来
  • 为主线程使用专用的连接
  • 在主线程的专用连接上不执行任何读写事务(ReadWrite transaction),只执行只读事务
  • 为读写操作建立单独的连接

相关 API 与操作

1. 删除

  • 删除指定集合的所有元素

    - (void)removeAllObjectsInCollection:(NSString *)collection
    
  • 删除数据库所有元素

    - (void)removeAllObjectsInAllCollections;
    
  • 删除某一个指定元素

    - (void)removeObjectForKey:(NSString *)key inCollection:(nullable NSString *)collection;
    
  • 删除某一些指定元素

    - (void)removeObjectsForKeys:(NSArray<NSString *> *)keys inCollection:(nullable NSString *)collection;
    

2. 获取总数

  • 获取集合总数

    - (NSUInteger)numberOfCollections;
    
  • 获取集合中 key 总数

    - (NSUInteger)numberOfKeysInCollection:(nullable NSString *)collection;
    
  • 获取数据库中 key 总数

    - (NSUInteger)numberOfKeysInAllCollections;
    

3. 获取列表

  • 获取所有集合的列表

    - (NSArray<NSString *> *)allCollections;
    
  • 获取某个集合中所有 key 的列表

    - (NSArray<NSString *> *)allKeysInCollection:(nullable NSString *)collection;
    

4. 获取对象与元数据

  • 获取指定对象

    - (nullable id)objectForKey:(NSString *)key inCollection:(nullable NSString *)collection;
    
  • 检测指定对象是否在集合中

    - (BOOL)hasObjectForKey:(NSString *)key inCollection:(nullable NSString *)collection;
    
  • 获取指定对象和元数据到指定地址,存在该 key 则返回 YES,否则返回 NO

    - (BOOL)getObject:(__nullable id * __nullable)objectPtr
             metadata:(__nullable id * __nullable)metadataPtr
               forKey:(NSString *)key
         inCollection:(nullable NSString *)collection;
    
  • 获取指定元数据

    - (nullable id)metadataForKey:(NSString *)key inCollection:(nullable NSString *)collection;
    

5. 获取原始数据

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

  • 获取指定对象

    - (nullable NSData *)serializedObjectForKey:(NSString *)key inCollection:(nullable NSString *)collection;
    
  • 获取指定元数据

    - (nullable NSData *)serializedMetadataForKey:(NSString *)key inCollection:(nullable NSString *)collection;
    
  • 读取指定对象和元数据到指定地址

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

6. 枚举

  • 枚举集合

    - (void)enumerateCollectionsUsingBlock:(void (^)(NSString *collection, BOOL *stop))block;
    - (void)enumerateCollectionsForKey:(NSString *)key usingBlock:(void (^)(NSString *collection, BOOL *stop))block;
    
  • 枚举key

    - (void)enumerateKeysInCollection:(nullable NSString *)collection
                         usingBlock:(void (^)(NSString *key, BOOL *stop))block;
    - (void)enumerateKeysInAllCollectionsUsingBlock:(void (^)(NSString *collection, NSString *key, BOOL *stop))block;
    - (void)enumerateKeysAndObjectsInCollection:(nullable NSString *)collection
                                   usingBlock:(void (^)(NSString *key, id object, BOOL *stop))block;
    - (void)enumerateKeysAndMetadataInCollection:(nullable NSString *)collection
                                    usingBlock:(void (^)(NSString *key, __nullable id metadata, BOOL *stop))block;
    

7. 存储和更新

  • 存储

    - (void)setObject:(nullable id)object forKey:(NSString *)key inCollection:(nullable NSString *)collection;
    - (void)setObject:(nullable id)object
             forKey:(NSString *)key
       inCollection:(nullable NSString *)collection
       withMetadata:(nullable id)metadata;
    
  • 更新

    - (void)replaceObject:(nullable id)object forKey:(NSString *)key inCollection:(nullable NSString *)collection;
    - (void)replaceMetadata:(nullable id)metadata forKey:(NSString *)key inCollection:(nullable NSString *)collection;
    

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

相关文章

  • 010-YapDatabase 食用指南

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

  • 《毒液》食用指南

    毒液是什么? 它是一种呈现为粘液态的共生体 诞生在距离地球几光年之外的克林特星 它可以和宿主结合成一种全新的生物 ...

  • 人类食用指南

    一夜之间,谁能想到,动物竟成了世界的主宰,人类却沦为了它们的食物,它们也不再像刚开始那样生吞活剥,渐渐的讲求精细化...

  • cmder食用指南

    开箱 在官网http://cmder.net/下载,解压即可打开包装食用 把Cmder.exe所在的文件目录加入环...

  • Git 简易食用指南

    写在前面 一开始我们先聊一聊版本控制,什么是版本控制呢?版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定...

  • 青皮核桃食用指南

    今天路过一处公园,偶然发现了一棵高大的核桃树。现在正是青皮核桃上市的时节。站在这棵核桃树下,一抬头就能看到不少乒乓...

  • 低端物种食用指南

    敲门声响起时,我吓得差点将身体对折过去。 我站在门后面歪着脑袋,破开的喉咙不时发出一阵怪声,像破塑料袋在风中拉扯延...

  • 日本综艺食用指南

    啊啦啦啦啦大家好,我是今日决定担当科普角色的太宰不好治。 治哥本身特别喜欢看日本的各种综艺节目,杂七杂八的都看了不...

  • 安卓食用指南

    在这之前区分下软件的区别 不画圈的 这个样 花圈的 这个样 网站提供的通常是第二种花圈的单导入方法都一样 两种方法...

  • 葵瓜子食用指南

    这个宅在家的春节,你吃了多少瓜子呀? 呃,我没有停止工作,买的瓜子还没吃多少,太难受了。 听说老外不会嗑瓜子???...

网友评论

      本文标题:010-YapDatabase 食用指南

      本文链接:https://www.haomeiwen.com/subject/pyhlbxtx.html