최신 영문 문서를 보시려면 이 곳을 참고하세요.

시작하기

설치

수동 설치 (Objective‑C & Swift)

  1. Realm 최신 버전을 다운로드하고 압축을 풉니다.
  2. ios/ 또는 osx/디렉터리(대상 플랫폼에 따라 선택)에서 Realm.framework을 선택하여 Xcode 프로젝트의 File Navigator에 넣습니다. 이때, 선택된 대상 그룹의 폴더에 파일이 복사 되는 것을 꼭 확인하시고, Finish 버튼을 누릅니다.
  3. Xcode의 File Navigator에서 프로젝트를 클릭합니다. 어플리케이션 대상을 선택하고 Build Phases 탭으로 이동합니다. Link Binary with Libraries의 +를 클릭하여 libc++.dylib를 추가합니다.
  4. Swift와 함께 사용한다면 Swift/RLMSupport.swfit 파일을 Xcode의 File Navigator에 넣으신 후 Copy items if needed를 선택합니다.
  5. OSX 프로젝트에서 사용한다면 +를 클릭하여 New Copy Files Phase를 선택합니다. 해당 phase를 Copy Framework로 이름을 변경하고 DestinationFramework로 설정한 후 Realm.framework를 추가합니다.

CocoaPods를 통한 설치 (Objective‑C 전용)

만약 CocoaPods을 사용하신다면…

  1. Podfile에 다음 문장을 추가합니다: pod "Realm".
  2. 커맨드라인 프롬프트에서, pod install를 실행합니다.
  3. 프로젝트에 CocoaPods을 통해 생성된 .xcworkspace파일을 사용합니다.

Xcode 플러그인

제공되는 Xcode 플러그인은 새로운 Realm 모델을 쉽게 만들 수 있도록 도와줍니다.

Realm의 Xcode 플러그인을 설치하는 가장 쉬운 방법은 Alcatraz를 이용하여 “RealmPlugin”을 설치하는 방법입니다. 물론 릴리즈를 통해 제공하는 plugin/RealmPlugin.xcodeproj을 열어서 직접 설치하여 빌드도 가능합니다. 설치된 플러그인을 확인하기 위해서는 Xcode 재실행이 필요합니다. Xcode 메뉴에서 새 파일을 생성 (File > New > File… — or ⌘N) 할 때, 새 Realm 모델을 생성하는 옵션을 볼 수 있습니다.

Realm 브라우저

또한 Realm 데이터베이스를 읽고 수정할 수 있는 앱을 제공하고 있습니다. 릴리즈browser/아래에서 확인 할 수 있습니다.

Tools > Generate demo database 메뉴를 통해서 테스트용 데이터베이스와 더미 데이터를 생성할 수 있습니다.

API 레퍼런스

전체 API 레퍼런스에서 모든 클래스와 메소드 등의 정보를 찾아보세요.

예제

릴리즈examples/에서 Objective‑C, Swift 그리고 RubyMotion 어플리케이션의 예제를 확인 할수 있습니다. 예제에는 마이그레이션, UITableViewController의 사용법, 암호화, 명렁어 툴 및 그 이외의 Realm의 여러 기능 사용법을 포함하고 있습니다.

도움을 얻으려면

  • 커뮤니티 뉴스레터에 가입하여 일반적인 팁을 얻거나, 다른 사례를 배우고 Realm과 관련된 블로그 포스팅이나 튜토리얼 등을 확인 할 수 있습니다.
  • StackOverflow: #realm 태그가 포함된 이전 질문 등을 찾거나 새로운 질문을 등록할 수 있습니다.
  • Twitter: 공식 계정 @realm에 연락 하거나 또는 #realm 태그를 이용할 수 있습니다.
  • Email: docs/cocoa/latest [email protected].

모델

Realm 데이터 모델은 @property로 일반적인 Objective‑C 인터페이스를 사용하여 정의할 수 있습니다. 간단하게 서브클래스 RLMObject나 존재하는 모델 클래스를 Realm 데이터 모델 객체로 만들 수 있습니다. Realm 모델 객체는 다른 Objective‑C의 객체와 기능이 대체로 같습니다. 자신만의 메소드와 프로토콜을 추가할 수 있고 다른 객체와 동일하게 사용할 수 있습니다. 주요 제한은 생성한 쓰레드 안에서만 사용할수 있다는 점과 인스턴스 변수에 직접적으로 다른 관련된 속성에 접근할 수 없다는 점입니다.

만약 이미 제공 중인 Xcode 플러그인을 설치하였다면 “New File…” 다이얼로그를 통해서 인터페이스와 동작하는 파일의 템플릿을 확인 할 수 있습니다.

관계와 자료구조는 타깃 타입의 속성이나 RLMArray의 객체 리스트를 포함하여 간단하게 정의 할 수 있습니다.

#import <Realm/Realm.h>

@class Person;

// Dog model
@interface Dog : RLMObject
@property NSString *name;
@property Person   *owner;
@end
RLM_ARRAY_TYPE(Dog) // define RLMArray<Dog>

// Person model
@interface Person : RLMObject
@property NSString      *name;
@property NSDate        *birthdate;
@property RLMArray<Dog> *dogs;
@end
RLM_ARRAY_TYPE(Person) // define RLMArray<Person>

// Implementations
@implementation Dog
@end // none needed

@implementation Person
@end // none needed
import Realm

// Dog model
class Dog: RLMObject {
    dynamic var name = ""
    dynamic var owner: Person? // Can be optional
}

// Person model
class Person: RLMObject {
    dynamic var name = ""
    dynamic var birthdate = NSDate(timeIntervalSince1970: 1)
    dynamic var dogs = RLMArray(objectClassName: Dog.className())
}

실행할 때 Realm이 코드에 명시된 모든 모델을 분석하므로 사용하지 않는 모델이더라도 문법적으로 유효해야 합니다.

더 자세한 내용은 RLMObject에서 확인하세요.

속성 타입

Realm이 지원하는 속성 타입은 다음과 같습니다: BOOL, bool, int, NSInteger, long, long long, float, double, CGFloat, NSString, NSDate, NSData.

모델 관계를 지정하기 위해 RLMArray<Object>RLMObject를 사용할 수 있습니다. RLMObject 클래스의 상속도 지원합니다.

속성 특성

주의, Realm은 Objective‑C의 속성 특성 중에 nonatomic, atomic, strong, copy, weak, 등을 무시합니다. Realm은 내부적으로 이미 최적화된 스토리지 방법론을 가지고 있기 때문입니다. 그래서 오해의 소지를 피하기 위해서 이러한 속성의 특성을 모델에 사용하지 않을 것을 권고합니다. 그러나 만약 속성 특성을 설정하였다면 한 영역에 RLMObject을 추가하기 전까지는 사용할 수 있습니다. 사용자 지정의 getter와 setter는 문제 없이 사용 할 수 있지만 한 영역의 RLMObject에서는 사용이 불가능합니다.

사용자 지정 모델

모델 정보를 추가적으로 지정하기 위해 다음과 같은 클래스 메소드가 있습니다:

  • +attributesForProperty: 속성 타입의 인덱싱 설정과 같이 속성 특성을 제공하기 위해 재정의할 수 있습니다.
  • +defaultPropertyValues 객체가 생성될 때마다 초기값을 제공하기 위해 재정의할 수 있습니다.
  • +primaryKey 해당 모델의 기본키 설정을 위해 재정의할 수 있습니다. 기본키를 지정하면 효율적으로 객체를 검색 혹은 수정할 수 있고 고유값으로 설정할 수 있습니다.
  • ignoredProperties persisting model properties로부터 Realm을 유지하기 위해 재정의할 수 있습니다.

쓰기

모든 객체의 변경(추가, 수정, 삭제)는 쓰기 트랜잭션 내에서 처리됩니다.

Realm 객체는 보통의 객체와 같이 단독적으로 값을 지정하고 사용이 가능합니다. 쓰레드 사이에 객체를 공유하거나 실행 중인 앱 간에 재사용을 하기 위해서는 쓰기 트랜잭션 내에서 수행되는 작업으로 Realm에 객체를 저장해야만 합니다. 다음과 같은 방법으로 Realm에 객체를 추가할 수 있습니다:

// Create object
Person *author = [[Person alloc] init];
author.name    = @"David Foster Wallace";

// Get the default Realm
RLMRealm *realm = [RLMRealm defaultRealm];
// You only need to do this once (per thread)

// Add to Realm with transaction
[realm beginWriteTransaction];
[realm addObject:author];
[realm commitWriteTransaction];
// Create a Person object
let author = Person()
author.name = "David Foster Wallace"

// Get the default Realm
let realm = RLMRealm.defaultRealm()
// You only need to do this once (per thread)

// Add to the Realm inside a transaction
realm.beginWriteTransaction()
realm.addObject(author)
realm.commitWriteTransaction()

Realm에서 객체를 생성한 후에는 그 객체는 지속적으로 사용할 수 있습니다. 그리고 그것을 구성하는 모든 변경사항도 지속적으로 사용가능합니다. (반드시 쓰기 트랜잭션 내에서 이루어져야 합니다.) 쓰기 트랜잭션이 커밋되는 시점에 같은 Realm 객체를 사용하는 서로 다른 쓰레드에 변경사항이 반영됩니다.

이 점은 주의하시기 바랍니다. 동시에 각각의 블럭에 여러 쓰기가 진행 중이라면 해당 쓰레드는 블럭이 됩니다. 이 부분은 여러 다른 범용 솔루션과 비슷합니다. 그래서 이런 경우에는 좋은 적용 사례를 살펴보길 추천합니다. 즉, 쓰기에 대해서 쓰레드를 나누는 것을 말합니다.

Realm의 MVCC 설계 덕분에 쓰기 트랜잭션이 실행 중에는 읽기는 제한되지 않습니다. 한 번에 여러 쓰레드에서 동시 쓰기를 해야하지 않는 이상, 대량의 쓰기 트렌잭션으로 구성하기 보다는 다수의 세세한 쓰기 트랜잭션으로 구성하길 선호하실 겁니다.

자세한 내용은 RLMRealmRLMObject를 확인하세요.

쿼리

쿼리는 RLMObject 콜렉션을 포함하는 RLMResults 객체를 반환합니다. RLMResults는 NSArray와 매우 유사한 인터페이스를 제공하고 RLMResult에 포함된 객체는 인덱스 첨자로 접근할 수 있습니다. NSArray와 달리, RLMResults는 타입을 강제하고 하나의 RLMObject 서브클래스를 갖도록만 설계되었습니다. 자세한 내용은 RLMResults를 참조하세요.

모든 쿼리 (쿼리와 속성 접근을 포함하여)는 바로 처리되지 않습니다. 속성에 접근할 때에만 데이터를 읽습니다.

쿼리에 대한 결과는 데이터의 복사본이 아닙니다: 쿼리 결과의 수정(쓰기 트랜잭션과 함께)은 직접적으로 디스크에 반영합니다. 동일하게, RLMResult 내에 있는 RLMObject로부터 직접적으로 관계그래프를 탐색할 수 있습니다.

타입을 통한 객체 검색

Realm에서 가장 기본적인 객체 검색 메소드는 [RLMObject allObjects]이고, 이는 기본 Realm으로부터 같은 서브클래스 타입의 모든 RLMObject 인스턴스의 RLMResults를 반환합니다.

// Query the default Realm
RLMResults *dogs = [Dog allObjects]; // retrieves all Dogs from the default Realm

// Query a specific Realm
RLMRealm *petsRealm = [RLMRealm realmWithPath:@"pets.realm"]; // get a specific Realm
RLMResults *otherDogs = [Dog allObjectsInRealm:petsRealm]; // retrieve all Dogs from that Realm
// Query the default Realm
let dogs = Dog.allObjects()

// Query a specific Realm
let petsRealm = RLMRealm(path: "pets.realm")
let otherDogs = Dog.allObjectsInRealm(petsRealm)

조건 쿼리

만약에 이미 NSPredicate에 익숙하다면, Realm에서 조회하는 방법을 이미 알고 있습니다. RLMObject, RLMRealm, RLMArray 그리고 RLMResults 모두 NSArray를 조회했던 것과 같이 간단하게 NSPredicate 인스턴스, 조건 문자열이나 조건 형식의 문자열 전달로 특정 RLMObject 인스턴스를 조회할 수 있는 메소드를 제공합니다.

예를 들면, 아래는 이전의 예시로부터 확장하여 [RLMObject objectsWhere:]를 호출하여, 기본 Realm으로 부터 색이 ‘tan’이고 이름이 ‘B’로 시작하는 모든 dog를 검색할 수 있습니다:

// Query using a predicate string
RLMResults *tanDogs = [Dog objectsWhere:@"color = 'tan' AND name BEGINSWITH 'B'"];

// Query using an NSPredicate object
NSPredicate *pred = [NSPredicate predicateWithFormat:@"color = %@ AND name BEGINSWITH %@",
                                                     @"tan", @"B"];
tanDogs = [Dog objectsWithPredicate:pred];
// Query using a predicate string
var tanDogs = Dog.objectsWhere("color = 'tan' AND name BEGINSWITH 'B'")

// Query using an NSPredicate object
let predicate = NSPredicate(format: "color = %@ AND name BEGINSWITH %@", "tan", "B")
tanDogs = Dog.objectsWithPredicate(predicate)

애플의 Predicates Programming Guide에서 조건절에 대한 상세한 정보를 얻을 수 있습니다. Realm은 일반적인 조건절을 지원합니다:

  • 비교 연산자는 속성 이름 또는 피연산자가 될 수 있습니다. 피연산자 중 적어도 하나는 속성의 이름이어야 합니다.
  • 비교 연산자 ==, <=, <, >=, >, !=, BETWEEN는 int, long, long long, float, double, NSDate 속성타입을 지원합니다. 예. age == 45
  • 비교 연산자 ==, !=, 예. [Employee objectsWhere:@”company == %@”, company]
  • 비교 연산자 ==, !=는 bool 속성을 지원합니다.
  • NSString, NSData 속성을 위해서 ==, !=, BEGINSWITH, CONTAINS, ENDSWITH 연산자를 지원합니다. 예. name CONTAINS ‘Ja’
  • 대소문자를 구분하지 않는 string 비교. 예. name CONTAINS[c] ‘Ja’ 알림. “A-Z”, “a-z”만이 대소문자 구분에서 제외됩니다.
  • Realm은 다음의 연산자 또한 지원합니다: “AND”, “OR”, “NOT”. 예. name BEGINSWITH ‘J’ AND age >= 32
  • 포함 연산자 IN. 예. name IN {‘Lisa’, ‘Spike’, ‘Hachi’}
  • Nil 비교 ==, !=. 예. [Company objectsWhere:@”ceo == nil”]. 객체를 대상으로한 관계에서만 유효합니다. 예를 들어, ceo is a property on the Company model.
  • ANY 비교. 예. ANY student.age < 21
  • 알림, 집합 표현 타입을 지원하지는 않지만 객체 값을 이용한 BETWEEN 연산자를 지원합니다. 예. RLMResults *results = [Person objectsWhere:@"age BETWEEN %@", @[42, 43]];

좀 더 상세한 내용은 [RLMObject objectsWhere:]을 통해 확인하세요.

결과값 정렬

RLMResults는 하나 혹은 여러 개의 속성으로 정렬 기준이나 순서를 설정하도록 지원합니다. 예를 들어, 아래는 [RLMObject objectsWhere:where:]의 결과값을 이름 알파벳순으로 정렬하는 코드입니다.

// Sort tan dogs with names starting with "B" by name
RLMResults *sortedDogs = [[Dog objectsWhere:@"color = 'tan' AND name BEGINSWITH 'B'"]
                               sortedResultsUsingProperty:@"name" ascending:YES];
// Sort tan dogs with names starting with "B" by name
var sortedDogs = Dog.objectsWhere("color = 'tan' AND name BEGINSWITH 'B'").sortedResultsUsingProperty("name", ascending: true)

[RLMObject objectsWhere:][RLMResults sortedResultsUsingProperty:ascending:]을 통해 상세 내용을 확인하세요.

연속 조회

Realm이 내세우는 조회 엔진의 장점은 각각의 연속적인 쿼리에 대한 데이터베이스 서버에 별도의 연결을 요구하는 기존의 데이터베이스와 비교하여 매우 적은 오버헤드로 연속 조회하는 기능입니다.

예를 들어, 만약에 ‘tan’색의 dog를 조회한후, ‘tan’색의 dog 중에서 이름이 ‘B’로 시작하는 dog를 찾기 원할 때 아래와 같이 연결된 쿼리로 조회가 가능합니다:

RLMResults *tanDogs = [Dog objectsWhere:@"color = 'tan'"];
RLMResults *tanDogsWithBNames = [tanDogs objectsWhere:@"name BEGINSWITH 'B'"];
let tanDogs = Dog.objectsWhere("color = 'tan'")
let tanDogsWithBNames = tanDogs.objectsWhere("name BEGINSWITH 'B'")

Realms

기본 Realm

항상 [RLMRealm defaultRealm]을 호출하여 realm 변수를 초기화하여 접근했습니다. 해당 메소드는 앱 내부 Documents 폴더 안에 “default.realm”에 대응되는 RLMRealm 객체를 반환합니다.

기본 Realm을 대상으로 사용자 지정 경로를 지정하고 싶다면 +[RLMRealm setDefaultRealmPath:]으로 설정할 수 있습니다. 앱을 테스트하거나 iOS 8 Shared Container으로 앱 간에 Realm을 공유할 때에 특히 유용합니다.

많은 Realm API 메소드는 RLMRealm 객체를 받는 버전과 기본 Realm을 사용하는 간편한 버전이 있습니다. 예를 들어, [RLMObject allObjects][RLMObject allObjectsInRealm:[RLMRealm defaultRealm]]는 동일합니다.

다른 Realms

간혹 여러 개의 Realm을 두는게 유용할 수도 있습니다. 예를 들어, 어플리케이션과 함께 파일을 모아놓을 필요가 있다면 읽기 전용 Realm을 하나 더 두어 분리할 수 있습니다. [RLMRealm realmWithPath:][RLMRealm realmWithPath:readOnly:error:]에서 좀 더 자세한 정보를 확인하세요.

[RLMRealm realmWithPath:]에 전달하는 파일 경로는 읽기 권한이 부여되어야 하니 주의하세요. Realm 파일을 저장하기에 가장 일반적인 경로는 iOS의 경우 “Documents”이며 OSX의 경우 “Application Support” 입니다. 다시 생성될 수 있는 파일은 <Application_Home>/Library/Caches 경로를 추천합니다. Apple iOS Data Storage 가이드라인을 따르세요.

하나의 Realm을 쓰레드 간에 공유하여 사용

여러 쓰레드에서 동일한 Realm 파일에 접근하기 위해서는 쓰레드마다 다른 Realm 객체를 두어야 하고 [RLMRealm defaultRealm], [RLMRealm realmWithPath:][RLMRealm realmWithPath:readOnly:error:]를 호출하셔야 합니다. 같은 경로를 지정할 경우에는, 모든 RLMRealm 객체는 디스크상의 같은 파일에 대응됩니다. 쓰레드 간에 Realm 객체 공유를 지원하지 않습니다. 같은 Realm 파일에 접근하는 RLMRealm 객체는 모두 같은 readOnly 값이어야 합니다. (혹은 모두 같은 readwrite).

In-Memory Realm

Realm은 기본적으로 디스크에 유지됩니다. 그러나 [RLMRealm inMemoryRealmWithIdentifier:] 팩토리 메소드를 사용해 순수하게 메모리에서 사용 할 수 있습니다.

RLMRealm *realm = [RLMRealm inMemoryRealmWithIdentifier:@"MyInMemoryRealm"];
let realm = RLMRealm.inMemoryRealmWithIdentifier("MyInMemoryRealm")

In-memory Realm은 영구적으로 데이터를 저장하지 않습니다. 하지만 조회, 관계, 쓰레드 보호를 포함한 Realm의 모든 기능이 동일하게 동작합니다. 디스크 영구보존을 위한 오버헤드를 피하려 한다면 유용한 옵션입니다.

주의: In-memory Realm의 대한 참조가 범위를 벗어나게 되면 모든 데이터는 메모리 해제됩니다. 앱이 구동되는 동안 In-memory Realm에 메모리 참조를 하도록 추천합니다.

관계

RLMObject는 RLMObject과 RLMArray<Object> 속성 사용으로 서로 연결할 수 있습니다. RLMArray는 NSArray와 매우 유사한 인터페이스를 제공하고 RLMArray에 포함된 객체는 인덱스 첨자로 접근할 수 있습니다. NSArray와 달리, RLMArray는 타입을 강제하고 하나의 RLMObject 서브클래스를 갖도록만 설계되었습니다. 자세한 내용은 RLMArray를 참조하세요.

Person 모델()이 정의되었고 Dog 모델을 같이 만들어 봅니다:

// Dog.h
@interface Dog : RLMObject
@property NSString *name;
@end
class Dog: RLMObject {
    dynamic var name = ""
}

대 일(To-One)

다 대 일 혹은 일 대 일 관계를 대상으로 간단하게 RLMObject 서브클래스 타입으로 속성을 정의합니다:

// Dog.h
@interface Dog : RLMObject
... // other property declarations
@property Person *owner;
@end
class Dog: RLMObject {
    ... // other property declarations
    dynamic var owner: Person?
}

다른 속성과 마찬가지로 이 속성도 사용할 수 있습니다.

Person *jim = [[Person alloc] init];
Dog    *rex = [[Dog alloc] init];
rex.owner = jim;
let jim = Person()
let rex = Dog()
rex.owner = jim

RLMObject 속성을 사용할 때, 일반 속성 문법으로 중첩 속성을 접근할 수 있습니다. 예를 들어 rex.owner.address.country 이 예제는 객체그래프를 탐색하고 자동적으로 Realm으로부터 필요한 객체를 반환합니다.

대 다(To-Many)

RLMArray<Object> 속성으로 대 다(To-Many) 관계를 정의할 수 있습니다. RLMArray는 하나의 RLMObject 타입을 갖고 NSMutableArray와 유사한 인터페이스를 제공합니다.

“dogs” 속성을 Person 모델에 추가하여 복수의 dogs와 연결하려면 RLMArray<Dog>타입을 먼저 정의해야 합니다. 이것은 모델 인터페이스에 해당하는 아래의 매크로를 통해 완료됩니다.

//Dog.h
@interface Dog : RLMObject
... // property declarations
@end

RLM_ARRAY_TYPE(Dog) // Defines an RLMArray<Dog> type
// Not needed in Swift

RLMArray<Dog>의 속성을 정의할 수 있습니다:

// Person.h
@interface Person : RLMObject
... // other property declarations
@property RLMArray<Dog> *dogs;
@end
class Person: RLMObject {
    ... // other property declarations
    dynamic var dogs = RLMArray(objectClassName: Dog.className())
}

RLMArray 속성에 이와 같이 접근하고 할당 할 수 있습니다:

// Jim is owner of Rex and all dogs named "Fido"
RLMArray *someDogs = [Dog objectsWhere:@"name contains 'Fido'"];
[jim.dogs addObjects:someDogs];
[jim.dogs addObject:rex];
let someDogs = Dog.objectsWhere("name contains 'Fido'")
jim.dogs.addObjects(someDogs)
jim.dogs.addObject(rex)

역관계

역관계(backlinks로도 알려진)로 특정 속성을 통해 주어진 객체와 연결된 모든 객체를 조회할 수 있습니다. 예를 들어, Dog 객체에서 -linkingObjectsOfClass:forProperty:를 호출하면 지정한 속성과 특정 클래스에 연결된 모든 객체를 반환합니다.

@interface Dog : RLMObject
@property NSString *name;
@property NSInteger age;
@property (readonly) NSArray *owners; // Realm doesn't persist this property because it is readonly
@end

@implementation Dog
// Define "owners" as the inverse relationship to Person.dogs
- (NSArray *)owners {
    return [self linkingObjectsOfClass:@"Person" forProperty:@"dogs"];
}
@end
class Dog: RLMObject {
    dynamic var name = ""
    dynamic var age = 0
    var owners: [Person] {
        // Realm doesn't persist this property because it only has a getter defined
        // Define "owners" as the inverse relationship to Person.dogs
        return linkingObjectsOfClass("Person", forProperty: "dogs") as [Person]
    }
}

알림

Realm 객체는 쓰기 트랜잭션이 커밋될 때마다 여러 쓰레드의 다른 객체에 알림을 보냅니다. 이러한 알림은 블록을 등록하여 구독할 수 있습니다:

// Observe Realm Notifications
self.token = [realm addNotificationBlock:^(NSString *note, RLMRealm * realm) {
    [myViewController updateUI];
}];
// Observe Realm Notifications
let token = realm.addNotificationBlock { note, realm in
    viewController.updateUI()
}

반환되는 알림 토큰에 레퍼런스를 참조하는 동안 만큼 알림은 구독됩니다. 토큰이 메모리 해제되었을 때 자동적으로 알림이 구독 취소되므로 업데이트를 등록한 클래스 내의 토큰에 참조를 걸어두세요.

보다 자세한 내용은 [Realm addNotificationBlock:][Realm removeNotificationBlock:] 을 확인하세요.

백그라운드 오퍼레이션

하나의 트랜잭션 내에서 동시에 여러 쓰기를 일괄 처리하여 많은 양의 데이터를 기록 할 때 Realm은 상당히 효율적입니다. 트랜잭션은 또한 메인 쓰레드를 블록킹하지 않도록 Grand Central Dispatch를 사용하여 백그라운드로 수행할 수 있습니다. RLMRealm 객체는 쓰레드로부터 안전하지 않은 쓰레드를 통해 공유 할 수 없습니다. 그래서 읽거나 쓰고자 하는 각 쓰레드/dispatch_queue에 RLMRealm 인스턴스를 받아야 합니다. 아래는 백그라운드 큐에 백만 개의 개체를 삽입하는 예입니다:

dispatch_async(queue, ^{    
    
  // Get realm and table instances for this thread
  RLMRealm *realm = [RLMRealm defaultRealm];
  
  // Break up the writing blocks into smaller portions
  // by starting a new transaction
  for (NSInteger idx1 = 0; idx1 < 1000; idx1++) {
    [realm beginWriteTransaction];
    
    // Add row via dictionary. Property order is ignored.
    for (NSInteger idx2 = 0; idx2 < 1000; idx2++) {
      [Person createInRealm:realm
                 withObject:@{@"name"      : [self randomString],
                              @"birthdate" : [self randomDate]}];
    }

    // Commit the write transaction
    // to make this data available to other threads
    [realm commitWriteTransaction];
  }
});
dispatch_async(queue) {
  // Get realm and table instances for this thread
  let realm = RLMRealm.defaultRealm()

  // Break up the writing blocks into smaller portions
  // by starting a new transaction
  for idx1 in 0..<1000 {
    realm.beginWriteTransaction()

    // Add row via dictionary. Property order is ignored.
    for idx2 in 0..<1000 {
      Person.createInDefaultRealmWithObject([
        "name": "\(idx1)",
        "birthdate": NSDate(timeIntervalSince1970: idx2)
      ])
    }

    // Commit the write transaction
    // to make this data available to other threads
    realm.commitWriteTransaction()
  }
}

상세한 내용은 RLMRealm에서 확인하세요.

REST APIs

Realm은 손쉽게 REST API와 연동되고 지역 지속성없이 REST API를 사용하는 경우와 달리 몇가지 장점을 갖습니다.

  • Realm을 이용해 하나의 API 호출로 대량의 데이터를 조회하거나 백그라운드를 통한 사전 조회를 가능하게 합니다.
  • Realm은 쓰레드 안전하기 때문에 쉽게 작업을 비동기 처리할 수 있고 REST 호출이 완료될 때 한번에 views를 수정할 수 있습니다.
  • 서버 측에서 복잡한 쿼리를 처리하는 것 대신에 Realm으로 부터 직접 데이터를 조회할 수 있습니다.
  • 오프라인일 때도 대량의 데이터를 저장해 놓을 수 있어 조회 및 수정을 할 수 있는 강력한 사용자 경험을 제공합니다.
  • 서버 측의 부하는 줄여줍니다: 처음에는 다소 많은 트래픽을 발생시킬 수 있지만 캐시 데이터를 사용함으로써 반복하여 같은 데이터를 가져오는 빈도를 줄여 서버 측에 부하를 줄여주게 됩니다.

좋은 적용 사례

  1. 비동기 요청 - 네트워크 요청과 다른 블록킹 오퍼레이션은 사용자 인터페이스의 블록킹을 막도록 백그라운드 쓰레드에서 동작해야 합니다. Realm에서 많은 수의 객체 입력과 수정은 백그라운드 쓰레드에서 하길 추천합니다. 백그라운드에서 일어나는 변경 사항에 대응하기 위해 알림를 사용할 수 있습니다.
  2. 대용량 데이터 캐싱 - 가능한 자주 사전조회하고 Realm에 로컬 저장하는 방식을 추천합니다. 이러한 방법은 지역적으로 모든 데이터셋에 쿼리를 가능케 합니다.
  3. 삽입 또는 수정 - 데이터셋이 기본키와 같은 고유 식별자를 가지고 있다면 REST API로부터 응답을 받았을 때 [RLMObject createOrUpdateInDefaultRealmWithObject:]를 삽입 혹은 수정 로직을 구현하는데에 손쉽게 사용할 수 있습니다. 이 메소드는 자동적으로 이미 레코드가 존재하는지 검사하고 새 레코드가 생성되는 동안에도 해당 레코드에 변경 사항이 적용됩니다.

예제

아래에는 REST API를 이용하여 Realm을 어떻게 사용할수 있는지에 대한 간단한 예제입니다. 이 예제에서는 foursquare API를 통해서 JSON형식의 데이터를 검색하고, Realm객체를 통해서 기본 Realm에 데이터를 저장합니다.

실시간으로 동작하는 비슷한 예제는 이곳의 비디오 데모를 통해서 확인이 가능합니다.

먼저 API를 통해 데이터셋을 가져오기 위해서 기본 Realm의 인스턴스를 생성합니다. 간단하게 이 예제에서 [NSData initWithContentsOfURL]를 사용했습니다.

// Call the API
NSData *response = [[NSData alloc] initWithContentsOfURL:
                    [NSURL URLWithString:@"https://api.foursquare.com/v2/venues/search?near=San%20Francisco&limit=50"]];

// Deserialize the response to JSON
NSDictionary *json = [[NSJSONSerialization
                       JSONObjectWithData:response
                                  options:kNilOptions
                                    error:&error] objectForKey:@"response"];
// Call the API
let url = NSURL(string: "https://api.foursquare.com/v2/venues/search?near=San%20Francisco&limit=50")
let response = NSData(contentsOfURL: url)

// De-serialize the response to JSON
let json = NSJSONSerialization.JSONObjectWithData(response,
    options: NSJSONReadingOptions(0),
      error: nil)["response"]

이와 같은 JSON 배열이 응답에 포함되어 있습니다:

{
  "venues": [
    {
      "id": "4c82f252d92ea09323185072",
      "name": "Golden Gate Park",
      "contact": {
        "phone": "4152522590"
      },
      "location": {
        "lat": 37.773835608329,
        "lng": -122.41962432861,
        "postalCode": "94103",
        "cc": "US",
        "state": "California",
        "country": "United States"          
      }
    }
  ]
}

Realm에는 이러한 JSON을 가져올 수 있는 몇가지 방법이 있습니다. 사용자 지정 삽입을 통해 NSDictionary를 읽고 하나의 RLMObject로 속성을 대응시킬 수 있습니다. 이 예제의 경우 Realm에 직접적으로 NSDictionary를 삽입하는 대신에 RLMObject의 계층구조에 때에 따라 자동적으로 대응되도록 만듭니다. 이 작업을 위해 JSON의 모든 키에 정확히 매칭시킬 RLMObject 구조의 속성이 필요합니다. JSON 키가 RLMObject의 속성에 매칭되지 않을 때에는 삽입되지 않습니다. 아래의 RLMObject 정의는 정상적으로 동작합니다:

// Contact.h
@interface Contact : RLMObject
@property NSString *phone;
@end

@implementation Contact
+ (NSString)primaryKey {
    return @"phone";
}
@end
RLM_ARRAY_TYPE(Contact)

// Location.h
@interface Location : RLMObject
@property double lat; // latitude
@property double lng; // longitude
@property NSString *postalCode;
@property NSString *cc;
@property NSString *state;
@property NSString *country;
@end

@implementation Location
@end
RLM_ARRAY_TYPE(Location)

// Venue.h
@interface Venue : RLMObject
@property NSString *id;
@property NSString *name;
@property Contact  *contact;
@property Location *location;
@end

@implementation Venue
+ (NSString)primaryKey {
    return @"id";
}
@end
RLM_ARRAY_TYPE(Venue)
class Contact: RLMObject {
    dynamic var phone = ""

    class func primaryKey() -> String! {
        return "phone"
    }
}

class Location: RLMObject {
    dynamic var lat = 0.0  // latitude
    dynamic var lng = 0.0  // longitude
    dynamic var postalCode = ""
    dynamic var cc = ""
    dynamic var state = ""
    dynamic var country = ""
}

class Venue: RLMObject {
    dynamic var id = ""
    dynamic var name = ""
    dynamic var contact = Contact()
    dynamic var location = Location()

    class func primaryKey() -> String! {
        return "id"
    }
}

결과 셋은 배열로 반환되기 때문에 [Venue createInDefaultRealmWithObject:] 호출을 통해 각 엘레멘트를 객체로 만들어야 합니다. 이렇게 JSON으로부터 Venue와 자식 객체가 만들어지고 새롭게 만들어진 객체는 기본 Realm에 추가됩니다.

//Extract the array of venues from the response
NSArray *venues = json[@"venues"];

RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
// Save one Venue object (and dependents) for each element of the array
for (NSDictionary *venue in venues) {
    [Venue createOrUpdateInDefaultRealmWithObject:venue];
}
[realm commitWriteTransaction];
//Extract the array of venues from the response
let venues = json["venues"] as [NSDictionary]

let realm = RLMRealm.defaultRealm()
realm.beginWriteTransaction()
// Save one Venue object (and dependents) for each element of the array
for venue in venues {
    Venue.createOrUpdateInDefaultRealmWithObject(venue)
}
realm.commitWriteTransaction()

마이그레이션

다른 어떤 데이터베이스로 작업할 때와 마찬가지로 데이터 모델은 시간이 지남에 따라 변경됩니다. 표준 Objective‑C 인터페이스로 Realm의 데이터 모델을 정의하고 다른 Objective‑C 인터페이스를 수정하는 것처럼 모델을 수정하기 간편합니다. 예를 들어, 아래와 같은 ‘Person.h’가 있다고 가정합니다:

@interface Person : RLMObject
@property NSString *firstName;
@property NSString *lastName;
@property int age;
@end
class Person: RLMObject {
    dynamic var firstName = ""
    dynamic var lastName = ""
    dynamic var age = 0
}

성과 이름을 따로 나누기 보다 ‘fullName’ 속성을 추가하길 바랍니다. 이러한 작업을 위해 다음과 같이 간단하게 객체 인터페이스를 수정합니다:

@interface Person : RLMObject
@property NSString *fullName;
@property int age;
@end
class Person: RLMObject {
    dynamic var fullName = ""
    dynamic var age = 0
}

이 시점에 어떠한 데이터라도 저장했다면 코드에 정의된 모델과 디스크에 기록된 모델 사이에 불일치가 발생합니다. 이러한 상황이 발생할 때 마이그레이션이 진행되지 않고 예외 처리됩니다.

마이그레이션의 실행

마이그레이션을 정의하고[RLMRealm setSchemaVersion:withMigrationBlock:] 호출로 관련된 버전을 정의합니다. 마이그레이션 블럭은 이전 스키마에서 새로운 스카마로 데이터 모델을 변경하는 모든 로직을 제공합니다. [RLMRealm setSchemaVersion:withMigrationBlock:] 호출한 후, 마이그레이션이 필요한 Realm은 자동으로 제공된 버전으로 수정된 마이그레이션 블럭을 적용합니다.

예를 들어, 상단의 Person 서브클래스를 마이그레이션한다고 가정합니다. 최소한의 필요한 마이그레이션 블럭은 아래와 같습니다.

// Inside your [AppDelegate didFinishLaunchingWithOptions:]

// Notice setSchemaVersion is set to 1, this is always set manually. It must be
// higher than the previous version (oldSchemaVersion) or an RLMException is thrown
[RLMRealm setSchemaVersion:1
            forRealmAtPath:[RLMRealm defaultRealmPath] 
        withMigrationBlock:^(RLMMigration *migration, NSUInteger oldSchemaVersion) {
  // We haven’t migrated anything yet, so oldSchemaVersion == 0
  if (oldSchemaVersion < 1) {
    // Nothing to do!
    // Realm will automatically detect new properties and removed properties
    // And will update the schema on disk automatically
  }
}];

// now that we have called `setSchemaVersion:withMigrationBlock:`, opening an outdated
// Realm will automatically perform the migration and opening the Realm will succeed
[RLMRealm defaultRealm];
// Inside your application(application:didFinishLaunchingWithOptions:)

// Notice setSchemaVersion is set to 1, this is always set manually. It must be
// higher than the previous version (oldSchemaVersion) or an RLMException is thrown
RLMRealm.setSchemaVersion(1, forRealmAtPath: RLMRealm.defaultRealmPath(),
                         withMigrationBlock: { migration, oldSchemaVersion in
  // We haven’t migrated anything yet, so oldSchemaVersion == 0
  if oldSchemaVersion < 1 {
    // Nothing to do!
    // Realm will automatically detect new properties and removed properties
    // And will update the schema on disk automatically
  }
})
// now that we have called `setSchemaVersion:withMigrationBlock:`, opening an outdated
// Realm will automatically perform the migration and opening the Realm will succeed
// i.e. RLMRealm.defaultRealm()

최소한 해야할 일은 Realm이 (자동적으로) 업그레이드한 스카마를 가르키는 빈 블럭과 버전을 수정하는 일입니다.

// Inside your [AppDelegate didFinishLaunchingWithOptions:]

[RLMRealm setSchemaVersion:1 
            forRealmAtPath:[RLMRealm defaultRealmPath] 
        withMigrationBlock:^(RLMMigration *migration, NSUInteger oldSchemaVersion) {
  // We haven’t migrated anything yet, so oldSchemaVersion == 0
  if (oldSchemaVersion < 1) {
    // The enumerateObjects:block: method iterates
    // over every 'Person' object stored in the Realm file
    [migration enumerateObjects:Person.className
                          block:^(RLMObject *oldObject, RLMObject *newObject) {    
      
      // combine name fields into a single field
      newObject[@"fullName"] = [NSString stringWithFormat:@"%@ %@",
                                         oldObject[@"firstName"],
                                         oldObject[@"lastName"]];          
    }];
  }
}];
// Inside your application(application:didFinishLaunchingWithOptions:)

RLMRealm.setSchemaVersion(1, forRealmAtPath: RLMRealm.defaultRealmPath(),
                         withMigrationBlock: { migration, oldSchemaVersion in
  if oldSchemaVersion < 1 {
    // The enumerateObjects:block: method iterates
    // over every 'Person' object stored in the Realm file
    migration.enumerateObjects(Person.className()) { oldObject, newObject in
      // combine name fields into a single field
      let firstName = oldObject["firstName"] as String
      let lastName = oldObject["lastName"] as String
      newObject["fullName"] = "\(firstName) \(lastName)"
    }
  }
})

마이그레이션이 성공적으로 완료되면 앱을 통해 Realm 및 모든 개체를 평소와 같이 접근 할 수 있습니다.

버전 추가

두 버전의 Person 클래스가 있다고 가정합니다:

// v0
@interface Person : RLMObject
@property NSString *firstName;
@property NSString *lastName;
@property int age;
@end

// v1
@interface Person : RLMObject
@property NSString *fullName; // new property
@property int age;
@end

// v2
@interface Person : RLMObject
@property NSString *fullName;
@property NSString *email;   // new property
@property int age;
@end
// v0
class Person: RLMObject {
    dynamic var firstName = ""
    dynamic var firstName = ""
    dynamic var age = 0
}

// v1
class Person: RLMObject {
    dynamic var fullName = "" // new property
    dynamic var age = 0
}

// v2
class Person: RLMObject {
    dynamic var fullName = ""
    dynamic var email = "" // new property
    dynamic var age = 0
}

마이그레이션 블록의 로직은 다음과 같습니다.

[RLMRealm setSchemaVersion:2 forRealmAtPath:[RLMRealm defaultRealmPath] 
                         withMigrationBlock:^(RLMMigration *migration, 
                                              NSUInteger oldSchemaVersion) {
  // The enumerateObjects:block: method iterates
  // over every 'Person' object stored in the Realm file
  [migration enumerateObjects:Person.className 
                        block:^(RLMObject *oldObject, RLMObject *newObject) {
    // Add the 'fullName' property only to Realms with a schema version of 0
    if (oldSchemaVersion < 1) {
      newObject[@"fullName"] = [NSString stringWithFormat:@"%@ %@",
                                oldObject[@"firstName"],
                                oldObject[@"lastName"]];
    }

    // Add the 'email' property to Realms with a schema version of 0 or 1
    if (oldSchemaVersion < 2) {
      newObject[@"email"] = @"";
    }
  }];
}];

// now that we have called `setSchemaVersion:withMigrationBlock:`, opening an outdated
// Realm will automatically perform the migration and opening the Realm will succeed
[RLMRealm defaultRealm];
RLMRealm.setSchemaVersion(2, forRealmAtPath: RLMRealm.defaultRealmPath(), 
                         withMigrationBlock: { migration, oldSchemaVersion in
  // The enumerateObjects:block: method iterates
  // over every 'Person' object stored in the Realm file
  migration.enumerateObjects(Person.className()) { oldObject, newObject in
    // Add the 'fullName' property only to Realms with a schema version of 0
    if oldSchemaVersion < 1 {
      let firstName = oldObject["firstName"] as String
      let lastName = oldObject["lastName"] as String
      newObject["fullName"] = "\(firstName) \(lastName)"
    }

    // Add the 'email' property to Realms with a schema version of 0 or 1
    if oldSchemaVersion < 2 {
        newObject["email"] = ""
    }
  }
})

// Realm will automatically perform the migration and opening the Realm will succeed
let realm = RLMRealm.defaultRealm()

데이터 스키마의 마이그레이션 구현을 더욱 자세한 내용은 마이그레이션 예제 앱을 확인하세요.

선형 마이그레이션

어떤 앱의 두 사용자 JP와 Tim이 있다고 가정해 봅니다. JP는 매우 자주 수정을 합니다만 Tim은 몇몇 버전은 건너뛰었습니다. JP는 앱의 모든 새 버전의 앱을 보고 매번 스키마를 업그레이드했을 것입니다. 그는 앱을 다운로드 할때마다, v0에서 v1으로 v1에서 v2로 수정했을 겁니다. 반면에 Tim이 다운로드를 하였을 경우에는 갑자기 v0에서 v2로 변경하여야 했을 겁니다. 따라서 시작하는 버전에 관계 없도록 비종속적으로 if (oldSchemaVersion < X)와 같이 모든 업그레이드가 보이도록 마이그레이션 블럭을 구조화 해야 합니다.

또 다른 시나리오는 버전을 건너뛰는 사용자에게서 발생 할 수 있습니다. 만약 Version 2에서 “email” 속성을 삭제하고, Version 3에서 다시 재도입하였고, 사용자는 Version 1에서 Version 3로 건너뛰었을 때, 그 코드 안의 해당 속성에 대한 스키마와 디스크상의 스키마에 불일치가 없어서 Realm은 “email” 속성의 삭제를 자동적으로 감지하지 못합니다. Tim의 Person 객체는 v3의 address 속성은 v1의 address 속성을 따라 갑니다. 이것은 v1 및 v3 사이의 속성에 대해서 내부 저장소의 표현을 변경(말하자면, ISO 주소 표현을 사용자 정의로 변경하는 것과 같은)하지 않는 한 문제가 되지 않을 수도 있습니다. 이와 같은 것을 피하기 위해서 모든 버전에 대해서 또는 “email” 속성에 대해 if (oldSchemaVersion < 3)와 같이 사용하여 모든 Realm이 version 3로 업그레이드 되어 정상적인 데이터셋을 가지도록 보장 할 것을 권고합니다.

디버깅

LLDB 지원과 실시간으로 Realm Browser을 통해 앱 내부 데이터를 볼 수 있어서 손쉽게 Realm 앱을 디버깅할 수 있습니다.

LLDB 지원 스크립트

속성 값이 nil 혹은 0으로만 표시되는 대신에 Xcode 플러그인에 Xcode UI에서 RLMObject, RLMResults, RLMArray 객체 디버깅을 지원하는 LLDB 스크립트가 포함되어 있습니다.

Xcode 스크린샷

알림: 해당 스크립트는 현재 Objective‑C만 지원합니다. Swift 지원을 준비 중입니다.

테스팅

Realm 앱 테스팅

Realm을 사용하고 테스트하기에 가장 적절한 방법은 기본 Realm을 사용하는 방법입니다. 테스트를 위해 어플리케이션 데이터를 덮어씌우기보다 간단하게 테스트 전에 +[RLMRealm setDefaultRealmPath:]를 호출하세요.

Realm과 관련된 코드를 테스트하는 또 다른 방법은 테스트하려는 모든 메소드가 RLMRealm 객체를 받고 테스트의 경우 다른 Realm 객체를 넘겨주는 방법입니다. 예를 들어, JSON API로부터 유저 정보를 GET 요청하는 메소드가 있다면 로컬 유저 정보가 즉각적으로 생성되는지 테스트할 수 있습니다:

// Application Code
+ (void)updateUserFromServer
{
  NSURL *url = [NSURL URLWithString:@"http://myapi.example.com/user"];
  [[[NSURLSession sharedSession] dataTaskWithURL:url
                               completionHandler:^(NSData *data,
                                                   NSURLResponse *response,
                                                   NSError *error) {
    [self createOrUpdateUserInRealm:[RLMRealm defaultRealm] withData:data];
  }] resume];
}

+ (void)createOrUpdateUserInRealm:(RLMRealm *)realm withData:(NSData *)data
{
  id object = [NSJSONSerialization JSONObjectWithData:data options:nil error:nil];
  [realm transactionWithBlock:^{
    [User createOrUpdateInRealm:realm withObject:object];
  }];
}

// Test Code
- (void)testThatUserIsUpdatedFromServer
{
  RLMRealm *testRealm = [RLMRealm realmWithPath:kTestRealmPath];
  NSData *jsonData = [@"{\"email\": \"[email protected]\"}"
                      dataUsingEncoding:NSUTF8StringEncoding];
  [ClassBeingTested createOrUpdateUserInRealm:testRealm withData:jsonData];
  User *expectedUser = [User new];
  expectedUser.email = @"[email protected]";
  XCTAssertEqualObjects([User allObjectsInRealm:testRealm][0],
                        expectedUser,
                        @"User was not properly updated from server.");
}
// Application Code
class func updateUserFromServer() {
  let url = NSURL(string: "http://myapi.example.com/user")
  NSURLSession.sharedSession().dataTaskWithURL(url) { data, _, _ in
    self.createOrUpdateUserInRealm(RLMRealm.defaultRealm(), withData: data)
  }
}

public class func createOrUpdateUserInRealm(realm: RLMRealm, withData data: NSData) {
  let object: [String: String] = 
          NSJSONSerialization.JSONObjectWithData(data, options: nil, error: nil)
  realm.transactionWithBlock {
    User.createOrUpdateInRealm(realm, withObject: object)
  }
}

// Test Code
func testThatUserIsUpdatedFromServer() {
  let testRealm = RLMRealm(path: kTestRealmPath)
  let jsonString: NSString = "{\"email\": \"[email protected]\"}"
  let jsonData = jsonString.dataUsingEncoding(NSUTF8StringEncoding)
  ClassBeingTested.createOrUpdateUserInRealm(testRealm, withData: jsonData)
  let expectedUser = User()
  expectedUser.email = "[email protected]"
  XCTAssertEqual(User.allObjectsInRealm(testRealm).first as User,
                 expectedUser,
                 "User was not properly updated from server.")
}

Realm 테스트

Realm은 모든 커밋마다 실행되는 테스트 단위가 있습니다. 그래서, Realm을 테스트하기보다는 앱에서 Realm 사용량을 테스트하는게 적절합니다.

Realm은 오픈소스이므로, 바인딩, 테스트, 문서에 대한 참여를 적극적으로 환영합니다.

추가 지원

예제를 통해서 앱상에서 Realm을 어떻게 사용하는지 확인 할 수 있습니다. (더욱 많은 예제가 준비되어 있습니다!)

해피 해킹! 언제나 realm-cocoa(영어)에서 개발자와 대화할 수 있습니다. 또한 Realm 페이스북 그룹에서 한국 개발자와 이야기하고, 한국어로 요청사항을 kr@realm.io으로 보내주세요!

현재 제약 사항

Realm은 현재 베타이며 1.0 공개를 향해 지속적으로 기능을 추가하고 문제점을 보완하고 있습니다. 가장 일반적인 제약 사항을 정리해놨습니다.

알려진 문제점을 알아보기 위해 GitHub 이슈에 방문해주세요.

Swift 프로젝트에선 CocoaPods으로 Realm이 설치되지 않습니다.

CocoaPods은 아직 Swift 프로젝트를 지원하지 않습니다. (이슈를 확인하세요. GitHub issue #2222). Swift 프로젝트에서 Realm 사용을 위해서는 위 지시사항을 따라주세요.

Fine-grained 알림은 지원하지 않습니다.

알림은 받아볼 수 있지만 (알림) 현재로써는 알림으로부터 무엇이 추가되고/삭제되고/옮겨지고/수정되었는지를 알 수 없습니다. 근시일 내에 이 기능을 추가합니다.

NSDate는 초 단위로 처리됩니다.

NSDate는 초 단위로 끊어서 처리됩니다. 현재 이 부분을 개선중입니다. 자세한 정보는 GitHub issue #875에 방문해주세요. 현재로는, NSTimeInterval 속성으로 손실없이 저장할 수 있습니다.

Realm 객체 Setter 및 Getter는 재정의할 수 없습니다.

Realm이 내부 데이터베이스로 속성을 다루는 위해 setter와 getter를 재정의하기 때문에 객체를 대상으로 재정의할 수 없습니다. 해결책은 재정의가 가능한 realm-ignore 속성을 새로 할당하고 호출하는 방법입니다.

KVO는 지원하지 않습니다.

Realm은 KVO를 지원하지 않지만 고유의 알림 방법을 가지고 있습니다. (확인하세요. 알림)

Realm 파일은 동시에 여러 프로세스에서 접근할 수 없습니다.

Realm 파일이 동시에 여러 쓰레드에서 접근이 가능하지만, 특정 시점에 하나의 프로세스에서만 접근할 수 있습니다. 이 부분은 iOS 8 extensions 과 OS X 어플리케이션의 영향입니다. 서로 다른 프로세스는 Realm 파일을 복사거나 프로세스별로 생성해야만 합니다. 멀티 프로세스 지원은 곧 예정되어 있습니다.

파일 크기 및 중간 버전 추적

SQLite보다 더 적은 용량으로 데이터를 디스크에 저장하길 기대하실 겁니다. Realm 파일이 크다면, Realm이 일시적으로 객체를 추적하지 않도록 invalidate을 호출해야할지도 모릅니다.

데이터의 일관된 뷰를 보여주기 위해 Realm은 오직 run loop 내의 interation 중에서 유효한 버전만을 수정합니다. 여러 다른 쓰레드에서 Realm에 쓰기 작업 중이고 데이터를 조회하고 오래 실행 중인 오퍼레이션에서 해당 쓰레드가 블로킹되고 있다면 해당 버전은 수정되지 않고 Realm은 필요하지 않을지도 모르지만 데이터의 중간 버전을 유지해야만 합니다. 그로인해 파일 크기가 점차 커지게 됩니다. (이러한 추가 공간은 나중에 쓰기 작업에 의해 다시 사용되거나 압축됩니다. 예를 들어, writeCopyToPath: 이러한 처리 대신에 invalidate를 호출하여 조회하고 있는 어떤 객체든 필요하지 않다고 명시적으로 전달할 수 있고 객체의 중간 버전을 추적하는 작업을 중단할 수 있습니다.

FAQ

Realm 라이브러리는 얼마나 큰가요?

한번 앱이 릴리즈를 위해 빌드가 되면, 원래의 크기에서 Realm은 오직 1MB 정도만 추가합니다. 배포되는 버전은 여러 아키텍쳐(ARM, ARM64, x86 for the simulator)를 지원하고 디버그 심볼 등을 포함하고 있어 상당히 크지만(iOS에서 37MB & OSX에서 2.4MB), 앱을 빌드하게 되면 Xcode에 의해 자동적으로 제거됩니다.

상용 어플리케이션에도 Realm을 사용할 수 있나요?

Realm은 2012년부터 상용 제품에 사용되었습니다.

Realm의 Objecitve-C와 Swift API가 커뮤니티의 피드백과 함께 더 많은 기능과 개선 사항으로 개선되고 있다고 기대하셔도 됩니다.

어떻게 Realm 내부에 저장된 데이터를 보호하나요?

몇가지 조건이 있긴 하지만 동시에 Realm으로 데이터를 암호화하는 몇가지 방법이 있습니다. 옵션을 살피기 위해 GitHub 코멘트를 보세요. 플랫폼 간에 호환되는 암호화는 다음 릴리즈에 추가됩니다.

Realm을 사용하기 위해 비용을 지불해야 하나요?

아닙니다. Realm은 상용 프로젝트를 포함하여 사용이 무료입니다.

어떻게 수익을 낼 계획입니까?

이미 기술을 중심으로 기업제품과 서비스를 판매하여 수익을 창출하고 있습니다. 만약 릴리즈나 realm-cocoa보다 현재상태에 대해 더욱 알고 싶으시다면, 이메일로도 대화를 할 수 있습니다. 그리고 공개적인 realm-cocoa를 개발하기 위해, 또한 무료 및 Apache 2.0 라이센스 하에 오픈소스로 유지 될 수 있도록 최선을 다하고 있습니다.

코드 안에 “tightdb”나 “core”로 참조되어 있는 것을 보았습니다. 이것은 무엇인가요?

TightDB는 핵심 C++ 스토리지 엔진의 옛 이름입니다. 그 코어는 현재 오픈 소스가 아니지만, 한번 내부를 정리하고, 이름을 정리하고, 내부의 동기화 구현을 완료한다면, 역시 Apache 2.0 라이센스로 오픈소스화를 할 계획입니다. 또한, 그 바이너리는 Realm Core(TightDB)의 바이너리 라이센스 내에서 사용 할 수 있습니다.

</div>