Repository: realm/realm-swift
File: Realm/RLMSchema.mm (Objective-C++)

Code before:
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

#import "RLMRealm_Private.hpp"
#import "RLMObjectSchema_Private.hpp"
#import "RLMSchema_Private.h"
#import "RLMObject.h"
#import "RLMUtil.hpp"

#import <objc/runtime.h>

NSString *const c_objectTableNamePrefix = @"class_";
const char *c_metadataTableName = "metadata";
const char *c_versionColumnName = "version";
const size_t c_versionColumnIndex = 0;

// RLMSchema private properties
@interface RLMSchema ()
@property (nonatomic, readwrite) NSMutableDictionary *objectSchemaByName;
@end

static RLMSchema *s_sharedSchema;

@implementation RLMSchema

- (RLMObjectSchema *)schemaForClassName:(NSString *)className {
    return _objectSchemaByName[className];
}

- (RLMObjectSchema *)objectForKeyedSubscript:(id <NSCopying>)className {
    RLMObjectSchema *schema = _objectSchemaByName[className];
    if (!schema) {
        NSString *message = [NSString stringWithFormat:@"Object type '%@' not persisted in Realm", className];
        @throw [NSException exceptionWithName:@"RLMException" reason:message userInfo:nil];
    }
    return schema;
}

- (id)init {
    self = [super init];
    if (self) {
        // setup name mapping for object tables
        _tableNamesForClass = [NSMutableDictionary dictionary];
        _objectSchemaByName = [NSMutableDictionary dictionary];
    }
    return self;
}

- (void)setObjectSchema:(NSArray *)objectSchema {
    _objectSchema = objectSchema;
    
    // update mappings
    for (RLMObjectSchema *object in objectSchema) {
        // set table name and mappings
        _tableNamesForClass[object.className] = RLMTableNameForClassName(object.className);
        [(NSMutableDictionary *)_objectSchemaByName setObject:object forKey:object.className];
    }
}

+ (void)initialize {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // load object schemas for all RLMObject subclasses
        unsigned int numClasses;
        Class *classes = objc_copyClassList(&numClasses);
        NSMutableArray *schemaArray = [NSMutableArray array];
        
        // cache descriptors for all subclasses of RLMObject
        RLMSchema *schema = [[RLMSchema alloc] init];
        for (unsigned int i = 0; i < numClasses; i++) {
            // if direct subclass
            if (class_getSuperclass(classes[i]) == RLMObject.class) {
                // add to class list
                RLMObjectSchema *object = [RLMObjectSchema schemaForObjectClass:classes[i]];
                [schemaArray addObject:object];
            }
        }
        free(classes);
        
        // set class array
        schema.objectSchema = schemaArray;
        
        // set shared schema
        s_sharedSchema = schema;
    });
}

// schema based on runtime objects
+(instancetype)sharedSchema {
    return s_sharedSchema;
}

// schema based on tables in a realm
+(instancetype)dynamicSchemaFromRealm:(RLMRealm *)realm {
    // generate object schema and class mapping for all tables in the realm
    unsigned long numTables = realm.group->size();
    NSMutableArray *schemaArray = [NSMutableArray arrayWithCapacity:numTables];
    
    // cache descriptors for all subclasses of RLMObject
    RLMSchema *schema = [[RLMSchema alloc] init];
    for (unsigned long i = 0; i < numTables; i++) {
        NSString *tableName = [NSString stringWithUTF8String:realm.group->get_table_name(i).data()];
        NSString *className = RLMClassForTableName(tableName);
        if (className) {
            tightdb::TableRef table = realm.group->get_table(i);
            RLMObjectSchema *object = [RLMObjectSchema schemaForTable:table.get() className:className];
            object->_table = table;
            [schemaArray addObject:object];
        }
    }
    
    // set class array and mapping
    schema.objectSchema = schemaArray;
    return schema;
}


inline tightdb::TableRef RLMVersionTable(RLMRealm *realm) {
    tightdb::TableRef table = realm.group->get_table(c_metadataTableName);
    if (table->get_column_count() == 0) {
        // create columns
        table->add_column(tightdb::type_Int, c_versionColumnName);
        
        // set initial version
        table->add_empty_row();
        (*table)[0].set_int(c_versionColumnIndex, 0);
    }
    return table;
}

NSUInteger RLMRealmSchemaVersion(RLMRealm *realm) {
    return (NSUInteger)(*RLMVersionTable(realm))[0].get_int(c_versionColumnIndex);

}

void RLMRealmSetSchemaVersion(RLMRealm *realm, NSUInteger version) {
    (*RLMVersionTable(realm))[0].set_int(c_versionColumnIndex, version);
}

- (id)copy {
    RLMSchema *schema = [[RLMSchema alloc] init];

    // copy object schema
    NSMutableArray *array = [NSMutableArray arrayWithCapacity:self.objectSchema.count];
    for (RLMObjectSchema *objSchema in self.objectSchema) {
        [array addObject:[objSchema copy]];
    }
    schema.objectSchema = array;
    
    return schema;
}

@end


Review comment (suggestion):
Calling `copy` on an NSArray will also call `copy` on all its objects. So this can be a single line:

``` objc
self.objectSchema = [schema.objectSchema copy];
```


Code after:
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

#import "RLMRealm_Private.hpp"
#import "RLMObjectSchema_Private.hpp"
#import "RLMSchema_Private.h"
#import "RLMObject.h"
#import "RLMUtil.hpp"

#import <objc/runtime.h>

NSString *const c_objectTableNamePrefix = @"class_";
const char *c_metadataTableName = "metadata";
const char *c_versionColumnName = "version";
const size_t c_versionColumnIndex = 0;

// RLMSchema private properties
@interface RLMSchema ()
@property (nonatomic, readwrite) NSMutableDictionary *objectSchemaByName;
@end

static RLMSchema *s_sharedSchema;

@implementation RLMSchema

- (RLMObjectSchema *)schemaForClassName:(NSString *)className {
    return _objectSchemaByName[className];
}

- (RLMObjectSchema *)objectForKeyedSubscript:(id <NSCopying>)className {
    RLMObjectSchema *schema = _objectSchemaByName[className];
    if (!schema) {
        NSString *message = [NSString stringWithFormat:@"Object type '%@' not persisted in Realm", className];
        @throw [NSException exceptionWithName:@"RLMException" reason:message userInfo:nil];
    }
    return schema;
}

- (id)init {
    self = [super init];
    if (self) {
        // setup name mapping for object tables
        _tableNamesForClass = [NSMutableDictionary dictionary];
        _objectSchemaByName = [NSMutableDictionary dictionary];
    }
    return self;
}

- (void)setObjectSchema:(NSArray *)objectSchema {
    _objectSchema = objectSchema;
    
    // update mappings
    for (RLMObjectSchema *object in objectSchema) {
        // set table name and mappings
        _tableNamesForClass[object.className] = RLMTableNameForClassName(object.className);
        [(NSMutableDictionary *)_objectSchemaByName setObject:object forKey:object.className];
    }
}

+ (void)initialize {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // load object schemas for all RLMObject subclasses
        unsigned int numClasses;
        Class *classes = objc_copyClassList(&numClasses);
        NSMutableArray *schemaArray = [NSMutableArray array];
        
        // cache descriptors for all subclasses of RLMObject
        RLMSchema *schema = [[RLMSchema alloc] init];
        for (unsigned int i = 0; i < numClasses; i++) {
            // if direct subclass
            if (class_getSuperclass(classes[i]) == RLMObject.class) {
                // add to class list
                RLMObjectSchema *object = [RLMObjectSchema schemaForObjectClass:classes[i]];
                [schemaArray addObject:object];
                // implement sharedSchema and className for this class
                RLMReplaceSharedSchemaMethod(classes[i], object);
                RLMReplaceClassNameMethod(classes[i], object.className);
            }
        }
        free(classes);
        
        // set class array
        schema.objectSchema = schemaArray;
        
        // set shared schema
        s_sharedSchema = schema;
    });
}

// schema based on runtime objects
+(instancetype)sharedSchema {
    return s_sharedSchema;
}

// schema based on tables in a realm
+(instancetype)dynamicSchemaFromRealm:(RLMRealm *)realm {
    // generate object schema and class mapping for all tables in the realm
    unsigned long numTables = realm.group->size();
    NSMutableArray *schemaArray = [NSMutableArray arrayWithCapacity:numTables];
    
    // cache descriptors for all subclasses of RLMObject
    RLMSchema *schema = [[RLMSchema alloc] init];
    for (unsigned long i = 0; i < numTables; i++) {
        NSString *tableName = [NSString stringWithUTF8String:realm.group->get_table_name(i).data()];
        NSString *className = RLMClassForTableName(tableName);
        if (className) {
            tightdb::TableRef table = realm.group->get_table(i);
            RLMObjectSchema *object = [RLMObjectSchema schemaForTable:table.get() className:className];
            object->_table = table;
            [schemaArray addObject:object];
        }
    }
    
    // set class array and mapping
    schema.objectSchema = schemaArray;
    return schema;
}


inline tightdb::TableRef RLMVersionTable(RLMRealm *realm) {
    tightdb::TableRef table = realm.group->get_table(c_metadataTableName);
    if (table->get_column_count() == 0) {
        // create columns
        table->add_column(tightdb::type_Int, c_versionColumnName);
        
        // set initial version
        table->add_empty_row();
        (*table)[0].set_int(c_versionColumnIndex, 0);
    }
    return table;
}

NSUInteger RLMRealmSchemaVersion(RLMRealm *realm) {
    return (NSUInteger)(*RLMVersionTable(realm))[0].get_int(c_versionColumnIndex);

}

void RLMRealmSetSchemaVersion(RLMRealm *realm, NSUInteger version) {
    (*RLMVersionTable(realm))[0].set_int(c_versionColumnIndex, version);
}

- (id)copyWithZone:(NSZone *)zone {
    RLMSchema *schema = [[RLMSchema allocWithZone:zone] init];
    schema.objectSchema = [[NSArray allocWithZone:zone] initWithArray:self.objectSchema copyItems:YES];
    return schema;
}

@end