GYDataCenter

이것 은 FMDB 데이터베이스 의 패 키 징 버 전 으로 메모리 캐 시, 데이터베이스 시트 자동 생 성 및 업데이트, 스 레 드 안전, 자동 일괄 기록 등 특징 을 가지 고 있 으 며 위 챗 독서 팀 작품 으로 신뢰 할 수 있 습 니 다.공식 글 소개:http://wereadteam.github.io/2016/07/06/GYDataCenter/ 다음은 본인 이 원본 을 읽 은 기록 입 니 다.
GYDataContext, 데이터 의 추가 삭제, 수정 등 을 완료 합 니 다. 먼저 내부 개인 클래스 GYDataContextQueue 를 보십시오. 이것 은 FMDatabaseQueue 를 모방 하여 쓴 것 입 니 다. 직렬 대기 열 을 이용 하여 스 레 드 안전 을 보장 합 니 다.
@interface GYDataContextQueue : NSObject
@property (nonatomic, strong) NSMutableDictionary *cache;
@end

@implementation GYDataContextQueue {
    dispatch_queue_t _queue;
}
static const void * const kDispatchQueueSpecificKey = &kDispatchQueueSpecificKey;

- (instancetype)initWithDBName:(NSString *)dbName {
    self = [super init];
    if (self) {
        _queue = dispatch_queue_create([[NSString stringWithFormat:@"GYDataCenter.%@", dbName] UTF8String], NULL);
        dispatch_queue_set_specific(_queue, kDispatchQueueSpecificKey, (__bridge void *)self, NULL);
        _cache = [[NSMutableDictionary alloc] init];
    }
    return self;
}

- (void)dispatchSync:(dispatch_block_t)block {
    GYDataContextQueue *currentQueue = (__bridge id)dispatch_get_specific(kDispatchQueueSpecificKey);
    if (currentQueue == self) { // .....@1
        block();
    } else {
        dispatch_sync(_queue, block);
    }
}
@end

dispatch_queue_create 와 obbcsetAssociated Object 방법 은 유사 합 니 다. 대 의 는 대상 을 현재 대기 열 에 연결 하고 key 에 따라 꺼 내 는 것 입 니 다.dispatchSync 방법 에서 dispatchget_specific 는 현재 호출 대기 열 이 키 에 따라 연 결 된 값 을 가 져 오 는 것 입 니 다. @1 의 역할 은 현재 대기 열 에서 dispatch 를 다시 실행 하지 않도록 하 는 것 입 니 다.sync ( queue, block) 방법 으로 잠 금 을 방지 합 니 다.FMDB 에서
Get the currently executing queue (which should probably be nil, but in theory could be another DB queue
* and then check it against self to make sure we're not about to deadlock. 

GYDataContext 의 4 대 기초 작업 을 살 펴 보 겠 습 니 다.
1. 조회 조작
- (id)getObject:(Class)modelClass  properties:(NSArray*)properties
     primaryKey:(id)primaryKey {
    GYDataContextQueue *queue = [self queueForDBName:[modelClass dbName]];
    __block id object = nil;
    [queue dispatchSync:^{
        NSMutableDictionary *cache = [self tableCacheFromDBCache:queue.cache class:modelClass];
        object = [cache objectForKey:primaryKey];
        if (!object || ((id)object).isFault) {
            NSString *where = [self whereIdSqlForClass:modelClass];
            NSArray *objects = [_dbRunner objectsOfClass:modelClass
                                              properties:properties
                                                   where:where
                                               arguments:@[ primaryKey ]];
            object = [objects firstObject];
            if (object && !properties.count) {
                [cache setObject:object forKey:primaryKey];
            }
        }
    }];
    return object;
}

동기 화 대기 열 에서 메모리 캐 시 를 먼저 조회 하고 없 으 면 디스크 에서dbRunner 에서 찾 았 습 니 다. 메모리 캐 시 를 찾 았 습 니 다.
2. 쓰기 동작
- (void)saveObject:(id)object {
    Class modelClass = [object class];
    GYDataContextQueue *queue = [self queueForDBName:[modelClass dbName]];
    [queue dispatchSync:^{
        if (object.isSaving) {
            return;
        }
        [(id)object setValue:@YES forKey:@"saving"];
        [_dbRunner saveObject:object];
        NSMutableDictionary *cache = [self tableCacheFromDBCache:queue.cache class:modelClass];
        if (cache) {
            [cache setObject:object forKey:[(id)object valueForKey:[modelClass primaryKey]]];
        }
        [(id)object setValue:@NO forKey:@"saving"];
    }];
}

먼저 디스크 에 저장 한 다음 메모리 에 캐 시 합 니 다.쓰기 과정 에서 object 의 saving 필드 를 YES 로 설정 합 니 다. 이 방법 을 여러 번 호출 할 때 동시에 쓰 는 것 을 방지 하 는 것 처럼 보이 지만 직렬 대기 열 이 라면 이런 문제 가 없 을 것 입 니 다. 개인 적 으로 는 소 용이 없다 고 생각 합 니 다.
3. 삭제 작업
- (void)deleteObject:(Class)modelClass
          primaryKey:(id)primaryKey {
    GYDataContextQueue *queue = [self queueForDBName:[modelClass dbName]];
    [queue dispatchSync:^{
        NSMutableDictionary *cache = [self tableCacheFromDBCache:queue.cache class:modelClass];
        id object = [cache objectForKey:primaryKey];
        if (object) {
            [cache removeObjectForKey:primaryKey];
            [object setValue:@YES forKey:@"deleted"];
        }
        NSString *where = [self whereIdSqlForClass:modelClass];
        [_dbRunner deleteClass:modelClass
                         where:where
                     arguments:@[ primaryKey ]];
    }];
}

메모리 캐 시 를 삭제 하고 디스크 캐 시 를 삭제 합 니 다.주의해 야 할 것 은 이 대상 이 메모리 캐 시 에서 제거 되 었 지만 외부 에 서 는 변수 가 인용 되 어 방출 되 지 않 을 수 있 습 니 다. 대상 의 속성 deleted 를 YES 로 설정 하고 이 대상 이 삭제 되 었 음 을 나타 내 며 외부 에서 적당 한 시간 에 이 deleted 속성 을 판단 합 니 다.
4. 조작 수정
- (void)updateObject:(Class)modelClass set:(NSDictionary *)set  primaryKey:(id)primaryKey {
   GYDataContextQueue *queue = [self queueForDBName:[modelClass dbName]];
    [queue dispatchSync:^{
        NSMutableDictionary *cache = [self tableCacheFromDBCache:queue.cache class:modelClass];
        [cache removeObjectForKey:primaryKey];
        NSString *where = [self whereIdSqlForClass:modelClass];
        [_dbRunner updateClass:modelClass set:set where:where arguments:@[ primaryKey ]];
    }];
}

메모리 에 있 는 캐 시 를 먼저 삭제 하고 디스크 에 있 는 캐 시 를 업데이트 합 니 다.이 때 이 대상 의 메모리 캐 시가 없어 졌 습 니 다. 다음 조회 작업 을 할 때 다시 디스크 에 가서 최신 대상 을 조회 하고 메모리 에 캐 시 합 니 다.여 기 는 왜 메모리 캐 시 를 직접 업데이트 하지 않 고 오히려 구 부 러 졌 습 니까?인터페이스 디자인 의 문제 인 것 같 습 니 다. 여기 모델 클 라 스 는 하나의 대상 이 아니 라 하나의 유형 으로 직접 사용 할 수 없습니다.
GYDB Runner 는 데이터베이스 의 읽 기와 쓰기, 사무 처리 등 을 쓰 고 있 으 며, 다음은 데이터베이스 의 특성 점 1, 자동 일괄 쓰기 입 니 다.
- (void)autoTransactionForDatabaseInfo:(GYDatabaseInfo *)databaseInfo {
    databaseInfo.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, [databaseInfo.databaseQueue queue]);
    if (databaseInfo.timer) {
        [databaseInfo.databaseQueue asyncInDatabase:^(FMDatabase *db) {
            [db beginTransaction];
        }];
        dispatch_source_set_timer(databaseInfo.timer,
                                  dispatch_time(DISPATCH_TIME_NOW, kTransactionTimeInterval * NSEC_PER_SEC),
                                  kTransactionTimeInterval * NSEC_PER_SEC,
                                  NSEC_PER_MSEC);
        dispatch_source_set_event_handler(databaseInfo.timer, ^{
            if (databaseInfo.needCommitTransaction) {
                [databaseInfo.databaseQueue syncInDatabase:^(FMDatabase *db) {
                    [db commit];
                    [db beginTransaction];
                }];
                databaseInfo.needCommitTransaction = NO;
            }
        });
        dispatch_resume(databaseInfo.timer);
    }
}

타 이 머 를 켜 고 트 랜 잭 션 을 켜 면 1s 마다 이 시간 동안 의 모든 트 랜 잭 션 을 자동 으로 제출 합 니 다.물론 모든 작업 에 사물 이 필요 한 것 은 아 닙 니 다. 삭제 수정 할 때 databaseInfo. needCommitTransaction 은 YES 로 설정 되 어야 트 랜 잭 션 제출 을 통 해 데이터 뱅 크 의 정확성 을 확보 하고 조회 작업 을 할 때 트 랜 잭 션 이 없습니다.수 동 으로 사 무 를 추가 하지 않 으 면 SQLite 는 모든 SQL 의 실행 을 위해 사 무 를 만 들 고 성능 을 소모 합 니 다.여러 개의 SQL 실행 패 키 지 를 하나의 업무 에 포함 시 키 고 1s 마다 제출 하여 여러 개의 사 무 를 만 드 는 비용 을 피 할 수 있 습 니 다.읽 기와 쓰기 가 빈번 할 때 성능 이 크게 향상 된다.
2. 데이터베이스 테이블 을 자동 으로 생 성하 고 업데이트 할 때 데이터베이스 작업 을 수행 할 때 현재 대상 모델 이 지속 적 으로 저장 해 야 하 는 속성 을 비교 하고 데이터베이스 에 저 장 된 대상 의 속성 과 차이 점 을 비교 합 니 다. 현재 대상 속성 이 증가 할 때의 자동 업데이트 데이터베이스 테이블 구조 만 지원 하고 필드 를 삭제, 수정 하 는 데 무력 합 니 다. 이때 자신 이 새로 만 든 테이블 로 데이터 이전 을 완성 해 야 합 니 다.
3. 관계 형 property 와 faulting 은 데이터베이스 에서 Employee 를 조회 할 때 특정한 속성 depart. ment 에 대응 하 는 것 이 다른 관계 대상 Department 인 것 을 발견 하면 Department 를 조회 하지 않 고 Department 대상 을 만 들 고 department 속성 을 설정 하여 Employee 의 department 속성 이 Department 를 가리 키 도록 합 니 다.이 때 Department 대상 에 표 시 를 하고 fault 속성 을 YES 로 설정 합 니 다. Employee 의 depart. ment 속성 을 진정 으로 사용 할 때 해당 Department 의 fault 속성 이 YES 인 것 을 발견 하고 초기 화 되 지 않 은 값 임 을 알 고 데이터베이스 Department 를 찾 습 니 다.GYDataContext 의 조회 조작 을 구체화 해 보도 록 하 겠 습 니 다.
- (id)getObject:(Class)modelClass  properties:(NSArray*)properties
     primaryKey:(id)primaryKey {
    GYDataContextQueue *queue = [self queueForDBName:[modelClass dbName]];
    __block id object = nil;
    [queue dispatchSync:^{
        NSMutableDictionary *cache = [self tableCacheFromDBCache:queue.cache class:modelClass];
        object = [cache objectForKey:primaryKey];
        if (!object || ((id)object).isFault) {// ......@2
            NSString *where = [self whereIdSqlForClass:modelClass];
            NSArray *objects = [_dbRunner objectsOfClass:modelClass
                                              properties:properties
                                                   where:where
                                               arguments:@[ primaryKey ]];
            object = [objects firstObject];
            if (object && !properties.count) {
                [cache setObject:object forKey:primaryKey];
            }
        }
    }];
    return object;
}

처음에 Employee 대상 을 조 회 했 을 때 매개 변수 modelClass 는 Employee 이 고 @ 2 조건 은 object 가 nil 이 므 로 데이터베이스 에 가서 Employee 대상 을 조회 합 니 다. 내부 에서 Employee. department 가 가리 키 는 Department 대상 을 국부 적 으로 예화 하고 캐 시 합 니 다. isFault 를 YES 로 설정 하고 동시에 Employee. department 가 이 대상 을 가리 키 도록 합 니 다.사용자 가 Employee. department 에 구체 적 으로 사용 할 때 이 방법 에 다시 들 어 갑 니 다. 이때 매개 변수 modelclast 는 Department 대상 입 니 다.캐 시 를 찾 아 대응 하 는 대상 을 찾 았 으 나 isFault 가 YES 인 것 을 발견 하고 초기 화 되 지 않 은 대상 을 위해 데이터 베 이 스 를 찾 아 실례 화 되 었 습 니 다.Faulting 메커니즘 은 대상 을 꺼 낼 때 모든 관계 대상 과 관계 대상 의 관계 대상 을 꺼 내 는 것 을 피한다.이렇게 하면 조회 속 도 를 높 여 로드 에 필요 한 대상 을 지연 시 킬 수 있다.

좋은 웹페이지 즐겨찾기