최신 영문 문서를 보려면 이곳을 참고하세요.
Objective‑C 또는 Objective‑C와 Swift를 같이 사용한 앱에서 Realm을 사용하는 것을 찾고 있다면, Realm Objective‑C 를 참고하세요. Realm Objective‑C 와 Realm Swift를 동시에 사용하는 것은 지원하고 있지 않습니다.
Realm 은 효율적으로 안전하고 빠르고 지속적인 방법으로 앱의 model layer를 작성할 수 있습니다. 아래의 예제를 참고하세요.
// Define your models like regular Swift classes
class Dog: Object {
dynamic var name = ""
dynamic var age = 0
}
class Person: Object {
dynamic var name = ""
dynamic var picture: NSData? = nil // optionals supported
let dogs = List<Dog>()
}
// Use them like regular Swift objects
let mydog = Dog()
mydog.name = "Rex"
print("name of dog: \(mydog.name)")
// Persist your data easily
let realm = try! Realm()
try! realm.write {
realm.add(mydog)
}
// Query it from any thread
dispatch_async(dispatch_queue_create("background", nil)) {
realm.objects(Dog).filter("age > 8") // => Results<Dog>
}
지금 Core Data를 사용하는데 Realm으로 바꾸기를 생각하고 있다면, 우리는 어떻게 해결하는 지에 대해서 기사를 발표했습니다. 관련 기사
시작하기
Realm 다운로드 또는 소스코드를 GitHub에서 볼 수 있습니다.
요구사항
- iOS 8 이상 또는 OS X 10.9 이상 및 watchOS. (iOS 7 에서는 “dynamic frameworks”를 사용할 수 없기 때문에 지원을 하지 않습니다.) *
- Xcode 6.3 이상.
- Objective‑C와 Swift 1.2, Swift 2.0, Swift 2.1을 지원하고 있습니다. 현재 버전에서는 Swift 2.1을 대상으로 릴리즈가 되었습니다. Swift 2.0 과 Swift 1.2 을 사용하는 것은 가능하지만, 수동으로 전환하여 사용해야 합니다. 우리는 Swift 1.2에 대해서는 몇 달 동안만 지원을 할 예정입니다.
설치 (Swift 2.1)
- Realm의 최신버전을 다운로드하고, zip 압축을 풉니다.
- Xcode 프로젝트에서 “General” 설정으로 이동합니다.
ios/dynamic/
,watchos
또는osx/
디렉터리에서 “Embedded Binaries” 부분으로Realm.framework
를 옮깁니다. Copy items if needed 를 선택했는지 확인하고 Finish 버튼을 누릅니다. - 유닛 테스트 타깃의 “Build Settings” 탭 에서 “Framework Search Paths” 부분에
Realm.framework
의 parent 경로를 추가하세요. - iOS 또는 watchOS 프로젝트에서 Realm을 사용한다면, 앱 타깃의 “Build Phases” 탭에서 “Run Script Phase”를 새로 생성하고 Script 텍스트 필드에 다음 코드를 붙여넣습니다.:
bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Realm.framework/strip-frameworks.sh"
이 단계는 universal binaries 아카이빙할 때 App Store submission bug 를 해결해야합니다.
- CocoaPods 0.39.9 버전 또는 그 상위버전을 설치하세요.
- Podfile에
use_frameworks!
로 추가합니다. 그리고 응용 프로그램과 테스트 대상에 pod ‘RealmSwift’ 추가하십시오. - 커멘트 라인에
pod install
을 실행하세요. - 프로젝트에서 CocoaPods 의해 만들어진
.xcworkspace
파일을 엽니다.
- Carthage 0.9.2 버전 또는 그 상위버전을 설치하세요.
github "realm/realm-cocoa"
를 Cartfile에 추가합니다.carthage update
를 실행합니다.-
iOS : Xcode 프로젝트의 “General” 셋팅에서 “Linked Frameworks and Libraries” 부분에 Carthage/Build/iOS/ 디렉터리로
RealmSwift.framework
와Realm.framework
를 옮깁니다.OS X: Xcode 프로젝트의 “General” 셋팅에서 “Embedded Binaries” 부분에 Carthage/Build/Mac/ 디렉터리로
RealmSwift.framework
와Realm.framework
를 옮깁니다.watchOS : Xcode 프로젝트의 “General” 셋팅에서 “Embedded Binaries” Carthage/Build/watchOS/ 디렉터리로
RealmSwift.framework
와Realm.framework
를 옮깁니다. - iOS: 앱 타깃의 “Build Phases” 셋팅 탭에서 “+” 아이콘을 누르고 “New Run Script Phase” 선택합니다. 다음과 같이 실행 스크립트를 생성합니다:
/usr/local/bin/carthage copy-frameworks
그리고 “Input Files”에 사용할 프레임워크의 경로를 추가합니다. 예를 들면:
$(SRCROOT)/Carthage/Build/iOS/Realm.framework
$(SRCROOT)/Carthage/Build/iOS/RealmSwift.framework이 스크립트는 universal binaries 에 의해 App Store submission bug 를 해결해야합니다.
설치(Swift 2.0)
Swift 2.0 지원은 Realm을 소스에서 빌드시 사용이 가능합니다. 우리의 프리패키지는 Swift 1.2 & Swift 2.1만 지원을 합니다.
- Realm을 GitHub 저장소에서 클론합니다.
- 클론된 저장소에서 릴리즈 태그로 체크아웃을 하기 위해
git checkout 0.96.2
을 실행합니다. - 복제 된 저장소에서 frameworks를 빌드하기 위해 REALM_SWIFT_VERSION=2.0 sh build.sh build 를 실행합니다.
- 기존 프로젝트에 설치하는 경우 오래된
RealmSwift.framework
과Realm.framework
을 삭제하십시오. - Xcode에서 프로젝트를 클릭하고 프로젝트 설정의 “General”탭을 표시합니다. 압축을 푼 폴더 안에있는 ios 또는 osx 폴더에서
RealmSwift.framework
과Realm.framework
를 “Embedded Frameworks”에 드래그 앤 드롭합니다. 이 때, Copy items if needed 에 체크가 들어가있는 것을 확인하고 Finish 를 클릭하십시오. - 단위 테스트의 대상을 선택하고 “Build Settings”탭의 “Framework Search Paths”에
RealmSwift.framework
부모 폴더의 경로를 추가하십시오.
- CocoaPods 0.39.0 이상을 설치하세요.
- Profile에서 main과 test 타깃에
use_frameworks!
와pod 'RealmSwift'
을 추가하세요. - 커멘드라인에서
pod install
을 실행합니다. - 프로젝트에서 CocoaPods 의해 만들어진 .xcworkspace 파일을 엽니다.
설치(Swift 1.2)
계속 Swift 1.2의 지원을 swift-1.2 브렌치
에서 제공하고 있습니다. 하지만, 최대한 빨리 Swift 2 로 전환하는 것을 권장합니다.
- Realm을 GitHub 저장소에서 클론합니다.
- 복제 된 저장소에
REALM_SWIFT_VERSION=1.2 sh build.sh build
를 실행합니다. - 기존 프로젝트에 설치하는 경우 오래된
RealmSwift.framework
과Realm.framework
을 삭제하십시오. - Xcode에서 프로젝트를 클릭하고 프로젝트 설정의 “General”탭을 표시합니다. 압축을 푼 폴더 안에있는
build/ios/swift-1.2/
또는build/osx/swift-1.2/
폴더에서RealmSwift.framework
과Realm.framework
를 “Embedded Frameworks”에 드래그 앤 드롭합니다. 이 때, Copy items if needed 에 체크가 들어가있는 것을 확인하고 Finish 를 클릭하십시오. - 단위 테스트의 대상을 선택하고 “Build Settings”탭의 “Framework Search Paths”에
RealmSwift.framework
부모 폴더의 경로를 추가하십시오.
Podfile 응용 프로그램과 테스트 대상에 아래와 같이 추가하십시오 :
# You need to add both “Realm” & “RealmSwift”
use_frameworks!
pod 'Realm', :git => 'https://github.com/realm/realm-cocoa.git', :branch => 'swift-1.2'
pod 'RealmSwift', :git => 'https://github.com/realm/realm-cocoa.git', :branch => 'swift-1.2'
주의: Carthage를 통해 Swift 1.2에서 설치할 수 있는 대응의 RealmSwift의 최신 버전은 0.95.0입니다.
- Carthage 0.9.2 버전 및 이상의 버전을 설치하세요.
- Cartfile에
github "realm/realm-cocoa"
을 추가합니다. - 커멘드라인에서
carthage update
를 실행하세요. -
iOS : Xcode 프로젝트의 “General” 셋팅에서 “Linked Frameworks and Libraries” 부분에 Carthage/Build/iOS/ 디렉터리로
RealmSwift.framework
와Realm.framework
를 옮깁니다.OS X: Xcode 프로젝트의 “General” 셋팅에서 “Embedded Binaries” 부분에 Carthage/Build/Mac/ 디렉터리로
RealmSwift.framework
와Realm.framework
를 옮깁니다.watchOS : Xcode 프로젝트의 “General” 셋팅에서 “Embedded Binaries” Carthage/Build/watchOS/ 디렉터리로
RealmSwift.framework
와Realm.framework
를 옮깁니다. -
iOS: 앱 타깃의 “Build Phases” 셋팅 탭에서 “+” 아이콘을 누르고 “New Run Script Phase” 선택합니다. 다음과 같이 실행 스크립트를 생성합니다:
/usr/local/bin/carthage copy-frameworks
그리고 “Input Files”에 사용할 프레임워크의 경로를 추가합니다. 예를 들면:<p><code> $(SRCROOT)/Carthage/Build/iOS/Realm.framework <br/>$(SRCROOT)/Carthage/Build/iOS/RealmSwift.framework </code></p> 이 스크립트는 universal binaries 에 의해 [App Store submission bug](http://www.openradar.me/radar?id=6409498411401216) 를 해결해야합니다.
tvOS
tvOS는 현재 베타 버전이지만, 우리는 현재 Realm이 tvOS 위에서 어떻게 이용할 수 있는지 확인하고 있습니다. 만약 Realm을 tvOS에서 동작시키는 경우는 어디까지나 개발을 위해서만 이용을 하시고, 프로덕션 용도로는 아직 적합하지 않으니 주의하시길 바랍니다. 자세한 내용은 여기 Pull Request #2506을 참고하세요.
Realm 브라우저
Realm Swift 0.96이나 그 이상 버전에서 사용하는 경우, 현재 Mac App Store 버전에서는 Realm 파일을 열 수 없습니다. GitHub에서 최신 Realm Browser 를 다운로드 하여 이용하시기 바랍니다.
RealmBrowser는 .realm
데이터베이스를 읽고, 편집을 할 수 있는 Mac 응용 프로그램입니다.
Tools > Generate demo database 메뉴를 통해서 테스트용 데이터베이스와 샘플 데이터가 포함된 Realm 데이터베이스를 생성할 수 있습니다.
Realm 파일을 찾는데 도움이 필요하다면 StackOverflow 답변을 참고하세요.
Realm 브라우저는 Mac App Store에서 이용가능합니다. Realm Swift 0.96이나 그 이상 버전에서 사용하는 경우, 현재의 Mac App Store 버전에서는 Realm 파일을 열 수 없습니다. GitHub에서 최신 Realm Browser 를 다운로드 하여 이용하시기 바랍니다.
Xcode 플러그인
제공되는 Xcode 플러그인은 새로운 Realm 모델을 쉽게 만들 수 있도록 도와줍니다.
Realm의 Xcode 플러그인을 설치하는 가장 쉬운 방법은 Alcatraz를 이용하여 “RealmPlugin” 을 설치하는 방법입니다. 물론 release zip를 통해 제공하는 plugin/RealmPlugin.xcodeproj
을 열어서 직접 설치하여 빌드도 가능합니다. 설치된 플러그인을 확인하기 위해서는 Xcode 재실행이 필요합니다. Xcode 메뉴에서 새 파일을 생성 (File > New > File… — or ⌘N) 할 때, 새 Realm 모델을 생성하는 옵션을 볼 수 있습니다.
API 레퍼런스
Realm에서 사용할 수있는 모든 클래스와 메소드에 대해서는 API Reference 를 참조하십시오.
예제
최신 Realm 의 examples/
에서 Objective‑C, Swift 그리고 RubyMotion 어플리케이션의 예제를 확인 할수 있습니다. 예제에는 마이그레이션, UITableViewController의 사용법, 암호화, 명렁어 툴 및 그 이외의 Realm의 여러 기능 사용법을 포함하고 있습니다.
도움을 얻으려면
- 코드와 관련하여 도움이 필요하시나요? StackOverflow에 물어보세요. 우리는 적극적으로 모니터링과 질문에 대한 답을 해드립니다.
- 버그를 알리고 싶나요? 우리 저장소에 이슈를 등록하세요. 가능하면 Realm의 버전과 전체 로그, Realm 파일, 프로젝트를 포함하여 이슈에 보여주세요.
- 기능 요청이 있나요? 우리 저장소에 이슈를 등록하세요. 우리에게 이 기능이 무엇을 하는지, 왜 필요한지를 말씀해주세요.
모델
Realm 데이터 모델은 property로 일반적인 클래스들을 사용하여 정의할 수 있습니다. 간단하게 서브클래스 Object나 존재하는 모델 클래스를 Realm 데이터 모델 객체로 만들 수 있습니다. Realm 모델 객체는 다른 의 객체와 기능이 대체로 같습니다. 자신만의 메소드와 프로토콜을 추가할 수 있고 다른 객체와 동일하게 사용할 수 있습니다. 주요 제한은 생성한 쓰레드 안에서만 사용할수 있다는 점과 인스턴스 변수에 직접적으로 다른 관련된 속성에 접근할 수 없다는 점입니다.
관계와 자료구조는 타깃 타입의 속성이나 List의 객체 리스트를 포함하여 간단하게 정의 할 수 있습니다.
import RealmSwift
// Dog model
class Dog: Object {
dynamic var name = ""
dynamic var owner: Person? // Can be optional
}
// Person model
class Person: Object {
dynamic var name = ""
dynamic var birthdate = NSDate(timeIntervalSince1970: 1)
let dogs = List<Dog>()
}
실행할 때 Realm이 코드에 명시된 모든 모델을 분석하므로 사용하지 않는 모델이더라도 문법적으로 유효해야 합니다.
더 자세한 내용은 Object 에서 확인하세요.
지원하는 데이터 유형
Realm이 지원하는 속성 타입은 다음과 같습니다: Bool
, Int8
, Int16
, Int32
, Int64
, Double
, Float
, String
, NSDate
초단위, and NSData
.
CGFloat
은 플랫폼이 독립적이지 않기 때문에 사용하지 않도록 하세요.
String
, NSDate
, NSData
및 Object
형의 속성은 Optional을 지정할 수 있습니다. 숫자를 Optional로 지정하려면, RealmOptional에서 실제 값을 랩핑합니다.
관계
Object는 Object 와 List 속성 사용으로 서로 연결할 수 있습니다. Object는 Array와 매우 유사한 인터페이스를 제공하고 List에 포함된 객체는 인덱스 첨자로 접근할 수 있습니다. Array와 달리, List는 타입을 강제하고 하나의 Object 서브클래스만 갖도록 설계되었습니다. 자세한 내용은 List를 참조하세요.
Person 모델(앞 부분을 참고하세요)이 정의되었고 Dog 모델을 같이 만들어 봅니다:
class Dog: Object {
dynamic var name = ""
}
1 대 1의 관계
다 대 일 혹은 일 대 일 관계를 대상으로 간단하게 Object 서브클래스 타입으로 속성을 정의합니다:
class Dog: Object {
// ... other property declarations
dynamic var owner: Person?
}
다른 속성과 마찬가지로 이 속성도 사용할 수 있습니다.
let jim = Person()
let rex = Dog()
rex.owner = jim
Object 속성을 사용할 때, 일반 속성 문법으로 중첩 속성을 접근할 수 있습니다. 예를 들면, rex.owner.address.country
이 예제는 객체그래프를 탐색하고 자동적으로 Realm으로부터 필요한 객체를 반환합니다.
1 대 다의 관계
List속성으로 1 대 다의 관계를 정의할 수 있습니다. List는 하나의 Object 타입을 갖고 a mutable Array와 유사한 인터페이스를 제공합니다.
“dogs” 속성을 Person 모델에 추가하여 복수의 dogs와 연결하려면, List<Dog>
의 속성을 정의할 수 있습니다:
class Person: Object {
// ... other property declarations
let dogs = List<Dog>()
}
List 속성에 이와 같이 접근하고 할당 할 수 있습니다:
let someDogs = realm.objects(Dog).filter("name contains 'Fido'")
jim.dogs.appendContentsOf(someDogs)
jim.dogs.append(rex)
역관계
역관계(backlinks로도 알려진)로 특정 속성을 통해 주어진 객체와 연결된 모든 객체를 조회할 수 있습니다. 예를 들어, Dog
객체에서 Object().linkingObjects(_:forProperty:)
를 호출하면 지정한 속성과 특정 클래스에 연결된 모든 객체를 반환합니다. Dog
에 읽기 전용(computed) owners
속성을 정의하여 패턴을 단순화할 수 있습니다:
class Dog: Object {
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 linkingObjects(Person.self, forProperty: "dogs")
}
}
Optional 속성
String
, NSDate
, 그리고 NSData
속성은 표준 Swift 문법을 사용하여 Optional 형 또는 보통의 형태로 정의 할 수 있습니다. 숫자를 Optional로 지정하려면, RealmOptional에 정의하면 됩니다:
class Person: Object {
// Optional string property, defaulting to nil
dynamic var name: String? = nil
// Optional int property, defaulting to nil
// RealmOptional properties should always be declared with `let`,
// as assigning to them directly will not work as desired
let age = RealmOptional<Int>()
}
let realm = try! Realm()
try! realm.write() {
var person = realm.create(Person.self, value: ["Jane", 27])
// Reading from or modifying a `RealmOptional` is done via the `value` property
person.age.value = 28
}
RealmOptional
은 Int
, Float
, Double
, Bool
,과 모든 크기의 Int
(Int8
, Int16
, Int32
, Int64
)에 사용할 수 있습니다.
속성 특성
Realm 모델 프로퍼티들은 기본 데이터베이스 데이터에 접근되기 위해 접근 순서로 dynamic var
특성이 필요합니다.
두 가지 예외가 있다. dynamic
프로퍼티들의 동적 디스패치에 사용되는 Objective‑C 런타임에 표시할 수 없기 때문에 List
와 RealmOptional
프로퍼티들을 동적으로 정의할 수 없습니다. 또한 항상 let
을 사용하여 선언해야 합니다.
인덱스 속성
클래스 메소드의 Object.indexedProperties()
를 재정의하면 인덱스에 추가 속성을 지정할 수 있습니다:
class Book: Object {
dynamic var price = 0
dynamic var title = ""
override static func indexedProperties() -> [String] {
return ["title"]
}
}
기본키
클레스 메소드의 Object.primaryKey()
를 재정의하면 그 모델의 기본키를 지정할 수 있습니다. 기본키를 사용하여 객체를 효율적으로 검색하고 업데이트 할 수 있고 고유성을 유지할 수 있습니다. 한 객체에 하나의 기본키가 Realm에 추가되면, 기본키는 변경될 수 없습니다.
class Person: Object {
dynamic var id = 0
dynamic var name = ""
override static func primaryKey() -> String? {
return "id"
}
}
저장하지 않는 속성
클래스 메소드의 Object.ignoredProperties()
을 재정의함으로써 Realm에 저장되지 않는 속성을 지정할 수 있습니다. 저장하지 않는 속성은 Realm은 속성의 조작에 개입하지 않습니다. 즉 보통의 속성으로 인스턴스 변수에 값을 저장하고 getter / setter 메소드를 자유롭게 재정의 할 수 있습니다.
class Person: Object {
dynamic var tmpID = 0
var name: String { // computed properties are automatically ignored
return "\(firstName) \(lastName)"
}
dynamic var firstName = ""
dynamic var lastName = ""
override static func ignoredProperties() -> [String] {
return ["tmpID"]
}
}
쓰기
모든 객체의 변경(추가, 수정, 삭제)는 쓰기 트랜잭션 내에서 처리됩니다.
Realm 객체는 보통의 객체와 같이 단독적으로 값을 지정하고 사용이 가능합니다. 쓰레드 사이에 객체를 공유하거나 실행 중인 앱 간에 재사용을 하기 위해서는 쓰기 트랜잭션 내에서 수행되는 작업으로 Realm에 객체를 저장해야만 합니다.
트랜잭션은 무시할 수 없는 오버헤드가 발생하기 때문에 간으한 트랜잭션의 수는 최소화 하는 것이 바람직합니다.
트랜잭션은 disk I/O를 동시에 수행하는 경우엔 실패할 수 있기 때문에, Realm.write()
& Realm.commitWrite()
는 throws
메소드로 선언되어 있으며, 디스크 용량 부족 등으로 실패했을 경우에 오류에서 복구할 수 있습니다. 간단하게 하기 위해서 이 문서와 샘플 코드에서는 오류 처리를 하지 않지만, 실제 앱에서는 오류를 처리하고 필요에 따라 복구를 해야합니다.
객체 생성
Object의 서브 클래스로 정의한 모델을 인스턴스화하여 새로운 객체로 Realm에 저장합니다. 이 간단한 모델을 생각해 볼 수 있습니다:
class Dog: Object {
dynamic var name = ""
dynamic var age = 0
}
우리는 여러 가지 방법으로 새로운 객체를 만들 수 있습니다:
// (1) Create a Dog object and then set its properties
var myDog = Dog()
myDog.name = "Rex"
myDog.age = 10
// (2) Create a Dog object from a dictionary
let myOtherDog = Dog(value: ["name" : "Pluto", "age": 3])
// (3) Create a Dog object from an array
let myThirdDog = Dog(value: ["Fido", 5])
- 가장 명백한 건 (Objective‑C에서) alloc-init을 사용하거나 (Swift에서) 지정된 초기화를 사용하는 것입니다. 모든 속성들은 객체가 Realm에 추가되기 전에 설정해야한다는 것을 유의하세요.
- 객체는 고유한 키와 값을 사용하여 dictionary에 만들 수 있습니다.
- 마지막으로 Object 서브클래스는 배열을 사용하여 인스턴스화 할 수 있습니다. 배열의 값은 모델에서 상응하는 속성들과 같은 것이어야한다.
중첩 객체
객체가 속성이 Objects 또는 Lists 일 경우에, Dictionary 및/또는 중첩 배열을 사용하여 재귀적으로 설정할 수 있습니다. 단순하게 각 객체와 속성을 대표하는 Dictionary 또는 배열을 간단하게 대체할 수 있다:
// Instead of using already existing dogs...
let aPerson = Person(value: ["Jane", 30, [aDog, anotherDog]])
// ...we can create them inline
let anotherPerson = Person(value: ["Jane", 30, [["Buster", 5], ["Buddy", 6]]])
이는 중첩 배열 및 Dictionary의 조합을 위해 작동할 것 입니다. List 은 Object
만 포함하고, String
과 같은 기본 유형을 포함 할 수 없습니다.
객체 추가
다음과 같은 방법으로 Realm에 객체를 추가할 수 있습니다:
// Create a Person object
let author = Person()
author.name = "David Foster Wallace"
// Get the default Realm
let realm = try! Realm()
// You only need to do this once (per thread)
// Add to the Realm inside a transaction
try! realm.write {
realm.add(author)
}
Realm에서 객체를 생성한 후에는 그 객체는 지속적으로 사용할 수 있습니다. 그리고 그것을 구성하는 모든 변경사항도 지속적으로 사용가능합니다. (반드시 쓰기 트랜잭션 내에서 이루어져야 합니다.) 쓰기 트랜잭션이 커밋되는 시점에 같은 Realm 객체를 사용하는 서로 다른 쓰레드에 변경사항이 반영됩니다.
이 점은 주의하시기 바랍니다. 동시에 각각의 블럭에 여러 쓰기가 진행 중이라면 해당 쓰레드는 블럭이 됩니다. 이 부분은 여러 다른 범용 솔루션과 비슷합니다. 그래서 이런 경우에는 적절한 사례를 살펴보길 추천합니다. 즉, 쓰기에 대해서 쓰레드를 나누는 것을 말합니다.
Realm의 MVCC 설계 덕분에 쓰기 트랜잭션이 실행 중에는 읽기는 제한되지 않습니다. 한 번에 여러 쓰레드에서 동시 쓰기를 해야하지 않는 이상, 대량의 쓰기 트렌잭션으로 구성하기 보다는 다수의 세세한 쓰기 트랜잭션으로 구성하길 선호하실 겁니다.
See Realm and Object for more details. 자세한 내용은 Realm과 Object를 확인하세요.
객체 업데이트
Realm이 객체를 업데이트할 몇 가지 방법 모두 상황에 따라 서로 다른 트레이드 오프를 제공한다. 상황에 가장 적합한 하나를 선택합니다:
입력 업데이트
쓰기 트랜잭션 내에서 속성을 설정하여 모든 객체를 업데이트 할 수 있습니다.
// Update an object with a transaction
try! realm.write {
author.name = "Thomas Pynchon"
}
기본 키 객체를 업데이트
모델에 기본 키가 있다면, 객체를 업데이트 할 수 있거나 Realm().add(_:update:)
을 사용해 아직 존재하지 않을 경우에 새 번호를 추가할 수 있습니다.
// Creating a book with the same primary key as a previously saved book
let cheeseBook = Book()
cheeseBook.title = "Cheese recipes"
cheeseBook.price = 9000
cheeseBook.id = 1
// Updating book with id = 1
try! realm.write {
realm.add(cheeseBook, update: true)
}
기본 키 id의 1인 책이 데이터베이스에 없다면, 이 대신에 새로운 책을 만들 수 있습니다.
또한 기본 키와 함께 업데이트를 원하는 값의 일부를 전달하여 기본키와 함께 객체를 업데이트 할 수 있습니다:
// Assuming a "Book" with a primary key of `1` already exists.
try! realm.write {
realm.create(Book.self, value: ["id": 1, "price": 9000.0], update: true)
// the book's `title` property will remain unchanged.
}
Key-Value Coding (KVC)
Object, Result, and List 모두 key-value coding (KVC) 을 준수합니다. 런타임시 업데이트 할 수 있는 속성을 결정해야 할 때 유용 할 수 있습니다.
컬렉션에 KVC를 적용하는 것은 모든 아이텐에 대한 접근을 작성하는 동안 컬렉션을 통해 반복하는 오버 헤드 없이 대량으로 객체를 업데이트 할 수 있는 좋은 방법입니다.
let persons = realm.objects(Person)
try! realm.write {
persons.first?.setValue(true, forKeyPath: "isFirst")
// set each person's planet property to "Earth"
persons.setValue("Earth", forKeyPath: "planet")
}
객체 삭제
삭제할 객체를 쓰기 트랜젝션 내에서 Realm().delete(_:)
메서드에 전달하세요.
// let cheeseBook = ... Book stored in Realm
// Delete an object with a transaction
try! realm.write {
realm.delete(cheeseBook)
}
또한 Realm에 저장된 모든 객체를 삭제할 수 있습니다. Realm 파일이 효율적으로 이후 객체를 위해 그 공간으로 재사용할 디스크 사이즈를 유지할 것을 명심하세요.
// Delete all objects from the realm
try! realm.write {
realm.deleteAll()
}
쿼리
쿼리는 Object 콜렉션을 포함하는 Results
인스턴스를 반환합니다. Results 는 Array와 매우 유사한 인터페이스를 제공하고 RLMResult에 포함된 객체는 인덱스 첨자로 접근할 수 있습니다. Array 와 달리, Results 는 타입을 강제하고 하나의 Object 서브클래스를 갖도록만 설계되었습니다.
모든 쿼리 (쿼리와 속성 접근을 포함하여)는 바로 처리되지 않습니다. 속성에 접근할 때에만 데이터를 읽습니다.
쿼리에 대한 결과는 데이터의 복사본이 아닙니다: 쿼리 결과의 수정(쓰기 트랜잭션과 함께)은 직접적으로 디스크에 반영합니다. 동일하게, Results 내에 있는 Object로부터 직접적으로 관계그래프를 탐색할 수 있습니다.
Realm에서 가장 기본적인 객체 검색 메소드는 Realm().objects(_:)
이고, 이는 기본 Realm으로부터 같은 서브클래스 타입의 모든 Object 인스턴스의 Results를 반환합니다.
let dogs = realm.objects(Dog) // retrieves all Dogs from the default Realm
필터링
만약에 이미 NSPredicate에 익숙하다면, Realm에서 조회하는 방법을 이미 알고 있습니다. Objects, Realm, List, 그리고 Results 모두 NSArray를 조회했던 것과 같이 간단하게 NSPredicate 인스턴스, 조건 문자열이나 조건 형식의 문자열 전달로 특정 Object 인스턴스를 조회할 수 있는 메소드를 제공합니다.
예를 들면, 아래는 이전의 예시로부터 확장하여 Results().filter(_:...)
를 호출하여, 기본 Realm으로 부터 색이 ‘tan’이고 이름이 ‘B’로 시작하는 모든 dog를 검색할 수 있습니다:
// Query using a predicate string
var tanDogs = realm.objects(Dog).filter("color = 'tan' AND name BEGINSWITH 'B'")
// Query using an NSPredicate
let predicate = NSPredicate(format: "color = %@ AND name BEGINSWITH %@", "tan", "B")
tanDogs = realm.objects(Dog).filter(predicate)
애플의 Predicates Programming Guide에서 조건절에 대한 상세한 정보를 얻을 수 있고 우리의 NSPredicate Cheatsheet를 사용할 수 있습니다. Realm은 일반적인 조건절을 지원합니다:
- 비교 연산자는 속성 이름 또는 피연산자가 될 수 있습니다. 피연산자 중 적어도 하나는 속성의 이름이어야 합니다.
-
비교 연산자 ==, <=, <, >=, >, !=, BETWEEN는 int, long, long long, float, double, NSDate 속성타입을 지원합니다.
예. age == 45
-
비교 연산자 ==, !=,
예.
Results<Employee>().filter("company == %@", company)
-
비교 연산자 ==, !=는 boolean 속성을 지원합니다.
-
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 비교 ==, !=.
예.
Results<Company>().filter("ceo == nil")
. -
Nil 비교 ==, !=
예.
Results<Company>().filter("ceo == nil")
.nil
자체가 같은 SQL과 다르게 값이 없는 것보다nil
처럼 특별한 값으로 지정합니다. -
객체를 대상으로한 관계에서만 유효합니다. 이 예에서는 ceo은 Company에서 속성으로 정의되어 있다.
-
ANY 비교.
예. ANY student.age < 21
-
집계 표현 타입
@count
,@min
,@max
,@sum
와@avg
등을 List에서 지원을 합니다.예.
realm.objects(Company).filter("employees.@count > 5")
라 쓰면, 5명 이상의 직원을 가진 모든 회사를 찾을 수 있습니다.
좀 더 상세한 내용은 Results().filter(_:...)
을 통해 확인하세요.
정렬
Results는 하나 혹은 여러 개의 속성으로 정렬 기준이나 순서를 설정하도록 지원합니다. 예를 들어, 아래는 결과값을 이름 알파벳순으로 정렬하는 코드입니다.
// Sort tan dogs with names starting with "B" by name
let sortedDogs = realm.objects(Dog).filter("color = 'tan' AND name BEGINSWITH 'B'").sorted("name")
Results().filter(_:...)
과 Results().sorted(_:ascending:)
을 통해 상세 내용을 확인하세요.
연쇄
Realm이 내세우는 조회 엔진의 장점은 각각의 연속적인 쿼리에 대한 데이터베이스 서버에 별도의 연결을 요구하는 기존의 데이터베이스와 비교하여 매우 적은 오버헤드로 연속 조회하는 기능입니다.
예를 들어, 만약에 ‘tan’색의 dog를 조회한후, ‘tan’색의 dog 중에서 이름이 ‘B’로 시작하는 dog를 찾기 원할 때 아래와 같이 연결된 쿼리로 조회가 가능합니다:
let tanDogs = realm.objects(Dog).filter("color = 'tan'")
let tanDogsWithBNames = tanDogs.filter("name BEGINSWITH 'B'")
자동 업데이트
Results는 실시간으로 기본 데이터가 자동으로 뷰에 업데이트된다. 이는 결과가 다시 패치가 될 필요가 없다는 의미입니다. 쿼리에 영향을 주는 수정되는 객체는 즉시 결과가 반영이 됩니다.
let puppies = realm.objects(Dog).filter("age < 2")
puppies.count // => 0
try! realm.write {
realm.create(Dog.self, value: ["name": "Fido", "age": 1])
}
puppies.count // => 1
필터링과 연쇄의 모든 객체는 모두 Results에 적용됩니다.
Results은 Realm이 빠르고 효율적이게 유지하는 것에 더불어 코드를 더 간단하고 더 반응하게 합니다. 예를 들면, 만약에 뷰 컨트롤러가 쿼리의 결과에 의존한다면, 사용자가 Results에 저장할 수 있으며, 각각에 접근하기 전에 데이터를 새로 고칠 수 있는지 확인하지 않고도 접근할 수 있습니다.
Realm 데이터가 업데이트 될때 Results를 다시 가지고 올 필요 없이 UI가 갱신된다는 것을 알기 위해서 Realm notifications을 구독합니다.
경과가 자동으로 업데이트 되기 때문에 일정한 인덱스와 개수를 유지하는 것이 중요합니다. Results이 고정되어 있는 유일한 시간은 그 위에 빠르게 열거할때, 열거하면서 쿼리에 일치하는 객체가 변형된 경우입니다.
try! realm.write {
for person in realm.objects(Person).filter("age == 10") {
person.age++
}
}
또한, Results에서 작업을 수행하기 위해 key-value coding 사용합니다.
Realm
기본 Realm
Realm()
을 호출하여 realm
변수를 초기화하여 접근했습니다. 해당 메소드는 앱 내부 Documents 폴더 안에 “default.realm”에 대응되는 a Realm 객체를 반환합니다.
Realm 설정 (Configuration)
Realm 파일이 저장된 위치처럼 설정된 것들은 Realm.Configuration 통해 이루어집니다. 그 설정은 Realm 인스턴스가 필요할 때 마다 Realm(configuration: config)
에 전달 될 수 있습니다. 또는 Realm.Configuration.defaultConfiguration = config
와 같이 기본 Realm 에 사용하는 것을 설정할 수 있습니다.
예를 들어, 사용자들이 웹 백엔드에 로그인을 할 수 있는 앱을 가졌다고 가정하면, 당신은 계정 전환을 신속하게 지원하기 원합니다. 당신은 다음을 수행하여 기본 Realm을 사용할 자신의 Realm 파일에 각각의 계정을 줄 수 있습니다:
func setDefaultRealmForUser(username: String) {
var config = Realm.Configuration()
// Use the default directory, but replace the filename with the username
config.path = NSURL.fileURLWithPath(config.path!)
.URLByDeletingLastPathComponent?
.URLByAppendingPathComponent(username)
.URLByAppendingPathExtension("realm")
.path
// Set this as the configuration used for the default Realm
Realm.Configuration.defaultConfiguration = config
}
다른 Realm
간혹 여러 개의 Realm을 두는게 유용할 수도 있습니다. 어플리케이션과 함께 일부 데이터를 메인 Realm뿐만 아니라 Realm 파일에 모아놓을 필요가 있을지도 모른다. 다음과 코드를 사용하여 이 작업을 수행 할 수 있습니다:
let config = Realm.Configuration(
// Get the path to the bundled file
path: NSBundle.mainBundle().pathForResource("MyBundledData", ofType:"realm"),
// Open the file in read-only mode as application bundles are not writeable
readOnly: true)
// Open the Realm with the configuration
let realm = try! Realm(configuration: config)
// Read some data from the bundled Realm
let results = realm.objects(Dog).filter("age > 5")
지정 경로가 Realm을 초기화하는데 사용되는 경우에, 그것은 쓰기 권한이 있는 위치에 있어야 한다는 것을 주의하세요. Realm 파일을 저장하기에 가장 일반적인 경로는 iOS의 경우 “Documents”이며 OSX의 경우 “Application Support” 입니다. 다시 생성될 수 있는 파일은 <Application_Home>/Library/Caches
경로를 추천합니다. Apple iOS Data Storage 가이드라인을 따르세요.
In-Memory Realm
Realm은 기본적으로 디스크에 유지됩니다. 그러나 Realm.Configuration에서 경로보다 inMemoryIdentifier
에 설정하면, 순수하게 메모리에서 작동ㅎ할 수 있습니다.
let realm = try! Realm(configuration: Realm.Configuration(inMemoryIdentifier: "MyInMemoryRealm"))
In-memory Realm은 영구적으로 데이터를 저장하지 않습니다. 하지만 조회, 관계, 쓰레드 보호를 포함한 Realm의 모든 기능이 동일하게 동작합니다. 디스크 영구보존을 위한 오버헤드를 피하려 한다면 유용한 옵션입니다.
In-memory Realm은 프로세스 간 알림 같은 것들을 조정하기 위해 임시 디렉터리에 여러 파일들을 만듭니다. 운영 체제가 메모리에 의한 압력으로 디스크를 교체할 필요가 없는 한 실제 어떠한 데이터도 파일에 기록되지 않습니다.
주의: In-memory Realm의 대한 참조가 범위를 벗어나게 되면 모든 데이터는 메모리 해제됩니다. 앱이 구동되는 동안 In-memory Realm에 메모리 참조를 하도록 추천합니다.
오류 헨들링
일반적인 disk IO 처리와 마찬가지로, Realm 인스턴스 생성은 자원이 부족한 환경에서는 실패 할 수 있습니다. 실제로 각 스레드에서 처음 Realm 인스턴스를 만들려고 할 때 오류가 발생할 수 있습니다. 그 이후 접근에서는 스레드 마다 캐시된 인스턴스가 반환되기 때문에 실패하는 것은 아닙니다.
오류를 처리하고 복구하려면 Swift의 표준 오류 처리 구조인 do~try~catch
를 이용합니다.
do {
let realm = try Realm()
} catch let error as NSError {
// handle error
}
Realm 간의 객체 복사
Realm에 저장된 객체를 다른 Realm에 복사하려면 Realm().create(_:value:update:)
메소드에 원본 객체를 인수로 전달합니다. 예를 들면. Realm().create(MyObjectSubclass.self, value: originalObjectInstance)
와 같이 사용을 하면됩니다.
Realm 객체는 처음 만들어진 스레드에서 접근할 수 있기 때문에 이 복사본은 동일한 스레드에서 Realm이 작동된다는 것을 기억하세요.
Realm File 찾는 방법
여러분의 앱의 Realm 파일의 위치를 모르는 경우에는 StackOverflow 답변을 참고하세요.
Realm을 앱에 번들링
처음 부팅을 빠르게 하기 위해서 같은 앱에 초기 데이터를 통합하는 것이 많습니다. 다음은 Realm을 초기 데이터로 번들하는 예입니다.
-
먼저 초기 데이터가 들어있는 Realm을 제공합니다. 출시 때와 같은 데이터 모델을 정의하고 데이터를 넣습니다. Realm 파일은 크로스 플랫폼에서 사용할 수 있기 때문에 데이터의 작성은 OS X와 iOS 시뮬레이터에서 해도 문제 없습니다. (JSONImport example를 참고하세요.)
-
초기 데이터가 포함됨 코드의 끝부분에 Realm 파일을 복사하는 방법을 사용하고, Realm 파일 크기를 최적화하세요. (
Realm().writeCopyToPath(_:encryptionKey:)
를 참고) 이 메소드를 사용하여 Realm 파일을 복사하면 Realm 파일 크기를 줄일 수 있고 결국엔 앱의 크기가 가벼워지기 때문에 사용자가 더 빠르게 다운로드 할 수 있습니다. - 복사된 Realm 파일을 Xcode 프로젝트 내비게이터에 드래그 앤 드롭을 합니다.
- 프로젝트 설정 Build Phase 탭에서 “Copy Bundle Resources”에 Realm 파일을 추가합니다.
- 이 시점에서 추가한 Realm 파일에 앱에서 접근할 수 있습니다.
NSBundle.mainBundle().pathForResource(_:ofType:)
메소드를 사용하여 파일 경로를 가지고 옵니다. - 동봉한 Realm 데이터 파일이 고정 데이터만을 포함하여 변경할 필요가 없는 것이면, Realm.Configuration에서
readOnly = true
를 지정하여 읽기 전용으로 직접 번들 내의 파일을 열 수 있습니다. 반면에 초기 데이터를 변경할 필요가 있다면, 번들에서 문서 디렉터리 등에 Realm 파일을NSFileManager.defaultManager().copyItemAtPath(_:toPath:error:)
메소드로 복사하고 사용할 수 있습니다.
Realm 파일을 초기 데이터로 통합한 예로 migration sample app를 참고하세요.
Class Subsets
각 Realm 파일에 저장되는 모델의 정의를 구분하고 싶은 것이 있습니다.
에를 들어서 내부에 Realm을 사용하는 다른 구성 요소를 두 팀이 개발하고 있다면, 그들 간에 마이그레이션을 조정하고 싶지 않을 것입니다. 아래와 같이, Realm.Configuration의 objectTypes
을 이용해 각각의 Realm에 저장하는 모델 클래스를 제한할 수 있습니다:
let config = Realm.Configuration(objectTypes: [MyClass.self, MyOtherClass.self])
let realm = try! Realm(configuration: config)
스레드
별도의 스레드에서 Realm을 사용하고 있으면, Realm 모든 객체는 일반적인 객체처럼 동시성과 멀티 스레딩에 대한 걱정없이 처리할 수 있습니다. Realm(다른 스레드에 동시에 업데이트 된다고 해도)에 접근하기 위해 잠금이나 단독적으로 처리를 생각할 필요가 없습니다.
Realm은 동시 처리를 쉽게 처리 할 수 있도록 하기 위해서 각 스레드에서 항상 일관성있게 데이터를 반환합니다. 많은 스레드에서 Realm을 동시에 조작한다면, 각각의 스레드마다 스냅 샷 데이터를 반환하고 일관성이 없는 상태가 될 수 있습니다.
한가지 주의해야 할 것은 여러 스레드를 걸처 같은 Realm 인스턴스를 공유할 수 없다는 것입니다. 만약에 여러 스레드에서 동일한 객체에 접근할 필요가 있는 경우에는 각각의 스레드가 Realm 인스턴스를 지정해야합니다. (그렇지 않으면 데이터가 일관성적이지 않게 보입니다.)
다른 스레드에서 변경된 데이터를 반영하기
메인 UI 스레드(또는 Runloop의 스레드) 객체는 런루프가 돌 때마다 자동으로 다른 스레드에서 변경된 데이터가 반영됩니다. 아무때나 그 시점의 스냅 샷 데이터를 반환하고 다른 스레드에서 데이터가 변경되었는지를 걱정할 필요없이 항상 일관된 데이터를 볼 수 있습니다.
먼저 Realm 파일을 열 때, Realm의 상태는 마지막 커밋이 성공한 상태입니다. 그리고 다음 업데이트가 반영 될 때 까지 그대로입니다. Realm은 autorefresh
이 NO
인 경구가 아니라면 런루프가 돌아올때마다 자동으로 최신 데이터로 업데이트됩니다. 만약에 스레드가 런루프를 가지고 있지 않은 일반적인 백그라운드 스레드인 경우에는 최신의 데이터를 반영하기 위해서 [Realm.refresh()
메소드를 직접 호출해야 합니다.
또한 트랜잭션이 커밋된 경우에도 최신 데이터가 반영이 됩니다. ([Realm.commitWrite()
).
주기적으로 최신 데이터 반영이 실패면 트랜잭션이 “pinned”되어 디스크 공간의 재사용을 방해합니다. 그것은 파일 크기가 커지는 것을 초래할 수 있습니다. 이 현상에 대한 자세한 내용은 현재 버전의 제한 사항을 참조하세요.
스레드 간의 객체 전달
독립형 (영속되지 않은) Object의 인스턴스는 일반적으로 NSObject의 인스턴스가 됩니다. 스레드 전반에 객체를 전달해도 문제 없습니다.
Realm, Object, Results, 또는 List의 지속된 인스턴스는 생성된 스레드 내에 있어야 사용할 수 있습니다. 다른 스레드에서 사용되면 예외가 발생합니다. 이것은 Realm 트랜잭션을 분리하기 위해 필요한 것입니다. 그렇지 않으면 객체가 잠재적으로 광법위한 관계 그래프와 함께 다른 트랜잭션에서 스레드 사이를 통과하는 것을 결정하는 것은 불가능하다.
스레드 사이를 가로 질러 객체를 전달하는 방법을 소개합니다. 예를 들어 primary key인 객체는 기본키 값으로 표현할 수 있으며, Results는 NSPredicate
또는 쿼리 문자열로 표현할수 있다. 또, Realm는 Realm.Configuration로 표현할수 있다. 타깃 스레드는 스레드 세이프을 사용하여 { RLMRealm %>, Object, Results, List을 다시 가지고 올 수 있습니다. 다시 패치하는 것은 원래 스레드와 다를 수 있습니다. 대상 스레드의 버전에서 객체를 검색하니 유의하세요.
- Realm: 모든 속성, 클래스 메소드, initializers
- Object:
invalidated
,objectSchema
,realm
, class methods, 과 initializers. - Results:
objectClassName
과realm
. - List:
invalidated
,objectClassName
, 과realm
.
하나의 Realm을 쓰레드 간에 공유하여 사용
여러 쓰레드에서 동일한 Realm 파일에 접근하기 위해서는 쓰레드마다 다른 Realm 객체를 두어야 하고 새로운 Realm를 초기화하셔야 합니다. 같은 경로를 지정할 경우에는, 모든 RLMRealm 객체는 디스크상의 같은 파일에 대응됩니다.
쓰레드 간에 Realm 인스턴스 공유를 지원하지 않습니다. 같은 Realm 파일에 접근하는 Realm 인스턴스는 모두 같은 readOnly
값이어야 합니다. (혹은 모두 같은 readwrite이거나 모두 readonly).
하나의 트랜잭션 내에서 동시에 여러 쓰기를 일괄 처리하여 많은 양의 데이터를 기록 할 때 Realm은 상당히 효율적입니다. 트랜잭션은 또한 메인 쓰레드를 블록킹하지 않도록 Grand Central Dispatch를 사용하여 백그라운드로 수행할 수 있습니다. Realm 객체는 쓰레드로부터 안전하지 않은 쓰레드를 통해 공유 할 수 없습니다. 그래서 읽거나 쓰고자 하는 각 쓰레드/dispatch_queue에 Realm 인스턴스를 받아야 합니다. 아래는 백그라운드 큐에 백만 개의 개체를 삽입하는 예입니다:
dispatch_async(queue) {
autoreleasepool {
// Get realm and table instances for this thread
let realm = try! Realm()
// Break up the writing blocks into smaller portions
// by starting a new transaction
for idx1 in 0..<1000 {
realm.beginWrite()
// Add row via dictionary. Property order is ignored.
for idx2 in 0..<1000 {
realm.create(Person.self, value: [
"name": "\(idx1)",
"birthdate": NSDate(timeIntervalSince1970: NSTimeInterval(idx2))
])
}
// Commit the write transaction
// to make this data available to other threads
try! realm.commitWrite()
}
}
}
알림
Realm 객체는 쓰기 트랜잭션이 커밋될 때마다 여러 쓰레드의 다른 객체에 알림을 보냅니다. 이러한 알림은 블록을 등록하여 구독할 수 있습니다:
// Observe Realm Notifications
let token = realm.addNotificationBlock { notification, realm in
viewController.updateUI()
}
반환되는 알림 토큰에 레퍼런스를 참조하는 동안 만큼 알림은 구독됩니다. 토큰이 메모리 해제되었을 때 자동적으로 알림이 구독 취소되므로 업데이트를 등록한 클래스 내의 토큰에 참조를 걸어두세요.
보다 자세한 내용은 Realm().addNotificationBlock(_:)
and Realm().removeNotification(_:)
을 확인하세요.
Key-Value Observation
Realm 객체는 대부분의 속성에 대한 Key-Value Observing을 대응하고 있습니다. Object 서브 클래스의 모든 지속 (무시가 아닌) 속성들은 Object와 List에 무효화된 속성들과 함께 KVO를 대응합니다.
독립 Object의 서브 클래스의 객체의 Observing 속성은 다른 dynamic property와 같이 작동하지만, 등록된 옵저버를 갖는 동안 Realm에 (realm.add(obj)
또는 비슷한 메서드와 함께) 객체를 추가할 수 없다는 것을 명심하세요.
지속된 객체에 대한 Observing properties는 조금 다르게 작동합니다. 지속 된 객체에서 해당 속성의 변화 타이밍이 3가지가 있습니다. 속성에 값을 직접 대입할 때 realm.refresh()
메소드를 호출했을 때, 또는 다른 스레드가 트랜잭션을 커밋하여 자동으로 Realm이 업데이트 되었을 때, 그리고 다른 스레드에서 변경이 있었지만 그 전에 realm.beginWrite()
를 호출하여 트랜잭션을 시작했기 때문에 변경이 통지되지 않은 때 등이 있습니다.
직접 할당하는 경우 외에 2 개의 경우는 모든 다른 스레드에서 변경된 내용은 한 번에 적용이 됩니다. 따라서 KVO의 통지는 1회에 정리합니다. 도중에 변경 상태는 파기되므로 1에서 10까지 하나씩 수를 증가시키는 속성이 있는 경우에는 1에서 10으로 변경되면서 하나의 통보만 받게 됩니다. 트랜잭션 외부에서 속성이 변경 될 수 있으므로 -observeValueForKeyPath : ofObject : change : context :
메소드에서 지속 된 Realm 객체를 변경하는 것을 권장하지 않습니다.
NSMutableArray
속성과 달리 List 속성 변경을 observing 하려면 -mutableArrayValueForKey :
를 사용할 필요는 없습니다 (호환성을 위해 그 방법을 이용해도 동일하게 작동하도록하고 있지 있지만) . 직접 List 를 변경하는 메소드를 호출하면 (모니터링하고 있으면) 업데이트가 통지됩니다. 일반 속성과 달리 Realm의 List 속성은 KVO의 observing 대상으로하는 데 dynamic
으로 정의 할 필요가 없습니다.
샘플 코드에 간단한 Realm와 ReactiveCocoa (Objective‑C) 의 샘플 및 ReactKit (Swift) 의 샘플이 있으므로 참고하세요.
마이그레이션
다른 어떤 데이터베이스로 작업할 때와 마찬가지로 데이터 모델은 시간이 지남에 따라 변경됩니다. 표준 클레스로 Realm의 데이터 모델을 정의하고 다른 클레스를 수정하는 것처럼 모델을 수정하기 간편합니다. 예를 들어, 아래와 같은 Person
모델이 있다고 가정합니다:
class Person: Object {
dynamic var firstName = ""
dynamic var lastName = ""
dynamic var age = 0
}
성과 이름을 따로 나누기 보다 ‘fullName’ 속성을 추가하길 바랍니다. 이러한 작업을 위해 다음과 같이 간단하게 객체 인터페이스를 수정합니다:
class Person: Object {
dynamic var fullName = ""
dynamic var age = 0
}
이 시점에 어떠한 데이터라도 저장했다면 코드에 정의된 모델과 디스크에 기록된 모델 사이에 불일치가 발생합니다. 이러한 상황이 발생할 때 기존 파일을 열려고 하면 마이그레이션이 진행되지 않고 예외 처리됩니다.
마이그레이션 실행
마이그레이션을 정의하고 setSchemaVersion(_:_:_:)
설정로 관련된 버전을 정의합니다. 마이그레이션 블럭은 이전 스키마에서 새로운 스카마로 데이터 모델을 변경하는 모든 로직을 제공합니다. 이러한 설정으로 RLMRealm을 작성할 때, 마이그레이션 블록은 마이그레이션이 필요한 경우 주어진 스키마 버전에 RLMRealm을 업데이트가 적용됩니다.
예를 들어, 상단의 Person 서브클래스를 마이그레이션한다고 가정합니다. 최소한의 필요한 마이그레이션 블럭은 아래와 같습니다.
// Inside your application(application:didFinishLaunchingWithOptions:)
let config = Realm.Configuration(
// Set the new schema version. This must be greater than the previously used
// version (if you've never set a schema version before, the version is 0).
schemaVersion: 1,
// Set the block which will be called automatically when opening a Realm with
// a schema version lower than the one set above
migrationBlock: { 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
}
})
// Tell Realm to use this new configuration object for the default Realm
Realm.Configuration.defaultConfiguration = config
// Now that we've told Realm how to handle the schema change, opening the file
// will automatically perform the migration
let realm = try! Realm()
최소한 해야할 일은 Realm이 (자동적으로) 업그레이드한 스카마를 가르키는 빈 블럭과 버전을 수정하는 일입니다.
최소 허용 마이그레이션이 있지만, 우리는 아마도 무언가 의미있는 새로운 속성 (이 경우 fullName
) 을 채우기위한 블록을 사용하고 싶다. 마이그레이션 블록 내에서 우리는 특정 유형의 각 Object를 열거하는 Migration().enumerate(_:_:)
호출 할 수 있고, 필요한 마이그레이션 로직을 적용 할 수 있습니다. 각 열거형에 대한 기존 Object 인스턴스는 oldObject
변수에 접근할 수 있고, 업데이트된 인스턴스는 newObject
를 통해 접근할 수 있다:
// Inside your application(application:didFinishLaunchingWithOptions:)
Realm.Configuration.defaultConfiguration = Realm.Configuration(
schemaVersion: 1,
migrationBlock: { migration, oldSchemaVersion in
if (oldSchemaVersion < 1) {
// The enumerate(_:_:) method iterates
// over every Person object stored in the Realm file
migration.enumerate(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
// class Person: Object {
// dynamic var firstName = ""
// dynamic var firstName = ""
// dynamic var age = 0
// }
// v1
// class Person: Object {
// dynamic var fullName = "" // new property
// dynamic var age = 0
// }
// v2
class Person: Object {
dynamic var fullName = ""
dynamic var email = "" // new property
dynamic var age = 0
}
마이그레이션 블록의 로직은 다음과 같습니다:
Realm.Configuration.defaultConfiguration = Realm.Configuration(
schemaVersion: 1,
migrationBlock: { migration, oldSchemaVersion in
// The enumerateObjects:block: method iterates
// over every 'Person' object stored in the Realm file
migration.enumerate(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 = try! Realm()
데이터 스키마의 마이그레이션 구현에 대한 더욱 자세한 내용은 마이그레이션 예제 앱을 확인하세요.
선형 마이그레이션
어떤 앱의 두 사용자 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로 업그레이드 되어 정상적인 데이터셋을 가지도록 보장 할 것을 권고합니다.
암호화
최소 허용 마이그레이션 하는 동안, Realm의 암호화 API는 iOS 와 WatchKit, iO X 용으로 사용할 수 있지만, watchOS는 Realm 암호화 메커니즘을 사용한 <mach/mach.h>
와 <mach/exc.h>
API은 __WATCHOS_PROHIBITED
으로 표시되어 있기 때문에 사용할 수 없다.
우리는 이 문제에 대하여 레이더를 제기했습니다: rdar://22063654.
미국으로부터의 수출 제한이나 금수 조치가 있는 국가에 거주하는 경우 Realm 사용에 대한 제한이 있을 수 있으므로, 저희 라이센스의 수출 규정 준수 조항을 참고하십시오.
iOS에서 Realm 파일을 약간의 오버헤드를 감수하고 NSFileProtection
API 사용으로 암호화할 수 있습니다. 암호화에 대한 두가지 주요 주의점입니다. 1) Realm 파일을 플랫폼 간에 이식하지 않는다. (NSFileProtection
은 iOS 전용입니다.) 그리고 2) Realm 파일을 iOS 장비 내에서 사용자 암호로 암호화하지 않는다. 두가지 제한을 피하기 위해서는 (혹은 OS X 앱을 개발하고 있다면) Realm 암호화를 사용해야 합니다.
Realm은 Realm 생성 시에 64-바이트 암호화 키를 제공하면 AES-256+SHA2 방식으로 디스크 내 데이터베이스 파일의 암호화를 지원합니다.
// Generate a random encryption key
let key = NSMutableData(length: 64)!
SecRandomCopyBytes(kSecRandomDefault, key.length,
UnsafeMutablePointer<UInt8>(key.mutableBytes))
// Open the encrypted Realm file
let config = Realm.Configuration(encryptionKey: key)
do {
let realm = try Realm(configuration: config)
// Use the Realm as normal
let dogs = realm.objects(Dog).filter("name contains 'Fido'")
} catch let error as NSError {
// If the encryption key is wrong, `error` will say that it's an invalid database
fatalError("Error opening realm: \(error)")
}
디스크 내 저장된 모든 데이터를 필요시에 AES-256 방식으로 암호화하고 복호화합니다. 그리고 SHA-2 HMAC 방식으로 확인합니다. Realm 인스턴스를 생성할 때 동일한 암호화 키를 사용해야 합니다.
암호화 키를 생성하고 키체인에 저장하고 Realm에서 사용하기까지 면밀히 살펴보기 위해 우리의 암호화 샘플 앱을 보세요.
암호화된 Realm을 사용할 때에는 다소 속도 하락(대체로 10% 이하)이 존재합니다.
외부 크래쉬 리포터(Crashlytics, PLCrashReporter 등)는 암호화된 Realm을 열기 전에 등록되어야 하고 그렇지 않으면 잘못된 경고 알림을 받게 됩니다.
디버깅
Realm Swift API를 사용하는 경우 응용 프로그램의 디버깅은 LLDB 콘솔을 사용해야합니다.
주의해야하는 것은 Realm의 변수를 조사 LLDB 스크립트를 Xcode Plugin 을 통해 설치했다해도 Swift의 경우이 제대로 작동하지 않고 실제와 다른 데이터가 표시됩니다. 대신 LLDB 콘솔에서 po
명령을 사용하여 Realm에 저장된 데이터를 확인하십시오.
암호화된 Realm 디버깅하기
암호화 된 Realm을 사용하는 프로세스에 LLDB 세션을 연결하는 것은 현재 지원되지 않습니다. REALM_DISABLE_ENCRYPTION = YES
환경 변수를 설정하여이 문제를 피할 수 있습니다. 코드를 변경하지 않고 디버깅을 가능하게하기 위해이 환경 변수는 강제로 암호화 API를 비활성화합니다. 물론 보안을 유지하기 위해 이미 암호화 된 .realm 파일에 대해서는 적용되지 않습니다 ( File :: AccessError
예외가 발생합니다). 그러나 새로운 Realm 파일을 만들고 접근하는데 경우에 유용합니다.
테스팅
기본 Realm 설정을 변경
Realm을 사용하고 테스트하기에 가장 적절한 방법은 기본 Realm을 사용하는 방법입니다.
실제 응용 프로그램 데이터를 덮어 버리는 것을 막기 위해, 혹은 각 테스트의 상태가 다른 테스트에 영향을주는 것을 방지하기 위해 각각의 테스트에서 새로운 Realm 파일을 사용하도록 기본 Realm을 설정합니다.
import XCTest
// A base class which each of your Realm-using tests should inherit from rather
// than directly from XCTestCase
class TestCaseBase: XCTestCase {
override func setUp() {
super.setUp()
// Use an in-memory Realm identified by the name of the current test.
// This ensures that each test can't accidentally access or modify the data
// from other tests or the application itself, and because they're in-memory,
// there's nothing that needs to be cleaned up.
Realm.Configuration.defaultConfiguration.inMemoryIdentifier = self.name
}
}
RLMRealm 인스턴스 삽입
Realm과 관련된 코드를 테스트하는 또 다른 방법은 테스트하려는 모든 메소드가 Realm 객체를 받고 테스트의 경우 다른 Realm 객체를 넘겨주는 방법입니다. 예를 들어, JSON API로부터 유저 정보를 GET
요청하는 메소드가 있다면 로컬 유저 정보가 즉각적으로 생성되는지 테스트할 수 있습니다:
// Application Code
func updateUserFromServer() {
let url = NSURL(string: "http://myapi.example.com/user")
NSURLSession.sharedSession().dataTaskWithURL(url!) { data, _, _ in
let realm = try! Realm()
createOrUpdateUserInRealm(realm, withData: data!)
}
}
public func createOrUpdateUserInRealm(realm: Realm, withData data: NSData) {
let object = try! NSJSONSerialization.JSONObjectWithData(data, options: [])
as! [String: String]
try! realm.write {
realm.create(User.self, value: object, update: true)
}
}
// Test Code
let kTestRealmPath = "..."
func testThatUserIsUpdatedFromServer() {
let config = Realm.Configuration(path: kTestRealmPath)
let testRealm = try! Realm(configuration: config)
let jsonData = "{\"email\": \"help@realm.io\"}"
.dataUsingEncoding(NSUTF8StringEncoding)!
createOrUpdateUserInRealm(testRealm, withData: jsonData)
let expectedUser = User()
expectedUser.email = "help@realm.io"
XCTAssertEqual(testRealm.objects(User).first!,
expectedUser,
"User was not properly updated from server.")
}
테스트 타깃에서 Realm.framework과 테스트 코드의 링킹 회피하기
동적 프레임워크로 Realm을 사용하는 경우, 단위 테스트의 대상이 Realm 프레임워크를 찾을 수 있도록 해 둘 필요가 있습니다. 이를 위해 단위 테스트 “:Framework Search Paths”에 Realm.framework
의 부모 폴더 위치를 추가합니다.
"Object type '...' not persisted in Realm"
와 같은 예외 처리 문구와 함께 테스트가 실패했다면, Realm.framework
를 테스트 타깃에 링크했기 때문입니다. Realm을 테스트 타깃에서 제외하세요.
또한 모델 클래스 파일은 응용 프로그램의 대상 또는 프레임 워크의 대상 어느 한쪽에만 연결되어 있어야합니다.
그렇지 않으면 해당 클래스는 테스트 도중 중복 정의되고 디버기 하기 어려운 이슈가 생길 수 있습니다. (자세한 정보느 https://github.com/realm/realm-cocoa/issues/1350 에서 확인하세요.)
테스트하려는 모든 코드가 유닛테스트 타깃에 노출되도록 확인하세요. (Swift 1.2에서는 public
한정자를 사용, Swift 2.0 @testable
을 사용합니다.)
REST API
Realm은 손쉽게 REST API와 연동되고 지역 지속성없이 REST API를 사용하는 경우와 달리 몇가지 장점을 갖습니다.
- REST API는 항상 연결되어야 하는 반면에 Realm 내부에 데이터를 캐싱하면 연결이 끊어져 있어도 오프라인 경험을 제공할 수 있습니다.
- 모든 데이터를 Realm 내부에 캐싱하면 REST API만으로는 불가능한 지역적으로 쿼리하고 검색하며 사용성을 확장할 수 있습니다.
- Realm에 데이터를 저장하면 새로운 추가되거나 변경된 데이터만을 서버에 요청함으로써 서버 측면의 부담을 줄일 수 있습니다.
적절한 사례
- 비동기 요청 - 네트워크 요청과 다른 블록킹 오퍼레이션은 사용자 인터페이스의 블록킹을 막도록 백그라운드 쓰레드에서 동작해야 합니다. Realm에서 많은 수의 객체 입력과 수정은 백그라운드 쓰레드에서 하길 추천합니다. 백그라운드에서 일어나는 변경 사항에 대응하기 위해 알림를 사용할 수 있습니다.
- 대용량 데이터 캐싱 - 가능한 자주 사전조회하고 Realm에 로컬 저장하는 방식을 추천합니다. 이러한 방법은 지역적으로 모든 데이터셋에 쿼리를 가능케 합니다.
- 삽입 또는 수정 - 데이터셋이 기본키와 같은 고유 식별자를 가지고 있다면 REST API로부터 응답을 받았을 때
Realm().add(_:update:)
를 삽입 혹은 수정 로직을 구현하는데에 손쉽게 사용할 수 있습니다. 이 메소드는 자동적으로 이미 레코드가 존재하는지 검사하고 새 레코드가 생성되는 동안에도 해당 레코드에 변경 사항이 적용됩니다.
예제
아래에는 REST API를 이용하여 Realm을 어떻게 사용할수 있는지에 대한 간단한 예제입니다. 이 예제에서는 foursquare API를 통해서 JSON형식의 데이터를 검색하고, Realm객체를 통해서 기본 Realm에 데이터를 저장합니다.
실시간으로 동작하는 비슷한 예제는 이곳의 비디오 데모를 통해서 확인이 가능합니다.
먼저 API를 통해 데이터셋을 가져오기 위해서 기본 Realm의 인스턴스를 생성합니다. 간단하게 이 예제에서 [NSData initWithContentsOfURL]
를 사용했습니다.
// 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 = (try! NSJSONSerialization.JSONObjectWithData(response,
options: []) as! NSDictionary)["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 정의는 정상적으로 동작합니다:
class Contact: Object {
dynamic var phone = ""
override static func primaryKey() -> String? {
return "phone"
}
}
class Location: Object {
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: Object {
dynamic var id = ""
dynamic var name = ""
dynamic var contact = Contact()
dynamic var location = Location()
override static func primaryKey() -> String? {
return "id"
}
}
결과 셋은 배열로 반환되기 때문에 [Venue createInDefaultRealmWithObject:]
호출을 통해 각 엘레멘트를 객체로 만들어야 합니다. 이렇게 JSON으로부터 Venue
와 자식 객체가 만들어지고 새롭게 만들어진 객체는 기본 Realm에 추가됩니다.
// Extract the array of venues from the response
let venues = json["venues"] as! [NSDictionary]
let realm = try! Realm()
try! realm.write {
// Save one Venue object (and dependents) for each element of the array
for venue in venues {
realm.create(Venue.self, value: venue, update: true)
}
}
현재 제한 사항
Realm은 현재 베타이며 1.0 공개를 향해 지속적으로 기능을 추가하고 문제점을 보완하고 있습니다. 가장 일반적인 제한 사항을 정리해놨습니다.
알려진 문제점을 알아보기 위해 GitHub 이슈에 방문해주세요.
일반적인 제한
Realm은 사용성과 속도의 균형을 목표로 하고 있습니다. 이러한 목표를 달성하기 위해, 현실적인 제한이 Realm에서 정보를 저장하는 다양한 측면에서 존재합니다. 예를 들어:
- 클래스 이름은 0~63 바이트 길이이어야 합니다. UTF8 문자열을 지원합니다. 제한 길이를 넘어서게 되면 앱을 초기화할 때에 예외 처리됩니다.
- 속성 이름은 0~63바이트 길이이어야 합니다. UTF8 문자열을 지원합니다. 제한 길이를 넘어서게 되면 앱을 초기화할 때에 예외 처리됩니다.
- NSData 속성은 16MB 크기로 제한됩니다. 더 큰 데이터를 저장하기 위해서는 16MB 단위의 작은 파일로 나누거나 파일 시스템에 직접 저장하고 경로를 Realm에 저장하세요. 단일 속성이 16MB를 초과하는 데이터를 저장하려고 시도하면 예외 처리됩니다.
- NSDate 속성은 초 단위로만 저장할 수 있습니다. 자세한 정보는 NSDate entry in Current Limitations below에서 확인하세요.
- 단일 Realm 파일 크기는 iOS가 대응하는 앱의 메모리 영역 크기를 넘어설 수 없습니다. 이는 디바이스별로 다르고 해당 시점에 얼마나 메모리 공간이 단편화되었는지에 따라 다릅니다 (해당 이슈에 대한 레이더가 있습니다: rdar://17119975). 더 많은 데이터를 저장할 필요성이 있다면 다수의 Realm 파일과 연동할 수 있습니다.
세부적인 알림은 지원하지 않습니다.
알림은 받아볼 수 있지만 (알림) 현재로써는 알림으로부터 무엇이 추가되고/삭제되고/옮겨지고/수정되었는지를 알 수 없습니다. 근시일 내에 이 기능을 추가합니다. Key-Value Observing 는 개체마다 변경 알림으로 사용할 수 있습니다. 특정 개체가 삭제 된 것을 감지 할 수 있습니다.
NSDate는 초 단위로 처리됩니다.
NSDate
는 초 단위로 끊어서 처리됩니다. 현재 이 부분을 개선중입니다. 자세한 정보는 GitHub issue #875에 방문해주세요. 현재로는, NSTimeInterval
속성으로 손실없이 저장할 수 있습니다. 그때까지 밀리 초 이하의 정밀도를 가진 시간을 저장할 경우 NSTimeInterval
형의 속성을 사용하십시오.
Realm 객체 Setter 및 Getter는 재정의할 수 없습니다.
Realm이 내부 데이터베이스로 속성을 다루는 위해 setter와 getter를 재정의하기 때문에 객체를 대상으로 재정의할 수 없습니다. 해결책은 재정의가 가능한 realm-ignore 속성을 새로 할당하고 호출하는 방법입니다.
파일 크기 및 중간 버전 추적
SQLite보다 더 적은 용량으로 데이터를 디스크에 저장하길 기대하실 겁니다. Realm 파일의 용량이 생각보다 큰 경우 Realm는 오래된 기록 데이터를 조회할 것입니다.
데이터의 일관성을 유지하기 위해 Realm은 최신 데이터에 액세스 한 경우에만 기록을 업데이트합니다. 이는 다른 스레드가 많은 데이터를 긴 시간을 들여 쓰는 동안 데이터를 조회 하려고하면 기록이 업데이트되지 않고 이전 데이터를 읽을 수 있습니다. 결과적으로, 기록 중간 데이터가 증가 할 수 있습니다.
이 추가 공간은 결국 재사용되거나 압축됩니다. (예를 들면 Realm().writeCopyToPath(_:encryptionKey:)
을 사용하여 파일을 복사합니다. 그 때 자동으로 파일 크기가 최적화됩니다.)
이 문제를 방지하려면invalidate
메소드를 호출 Realm에 지금까지 얻은 데이터는 더 이상 필요 없어진 것을 알려주세요. 그렇다면 Realm은 중간 데이터의 기록을 해제합니다. 그리고 다음 액세스의 경우 최신 데이터를 사용하도록 Realm이 업데이트됩니다.
또한 GCD를 사용하여 Realm에 액세스 한 경우에도이 문제가 발생할 수 있습니다. 블록의 실행이 종료 된 후에도 오토릴리즈풀 개체가 해제되지 않고 RLMRealm
가 해제 될 때까지 오래된 기록 데이터가 남게됩니다.
이 문제를 피하기 위해 GCD 블록에서 Realm에 액세스 할 때 명시적으로 오토 릴리즈 풀을 이용하십시오.
Objective‑C에서 List 속성에 액세스 할 수 없습니다
Realm Swift를 Objective‑C에서 사용하는 경우 List
속성을 private
또는 internal
로 지정해야합니다. 이것은 현재 확인 된 Swift의 결함 으로 인해 자동으로 생성되는 Objective‑C 헤더 ( -Swift.h
)는 컴파일 할 수 없습니다. 자세한 내용은 GitHub issue # 1925 을 참조하십시오.
Xcode 7 Beta 5에서이 문제가 해결되었습니다.
FAQ
Realm 라이브러리는 얼마나 큰가요?
한번 앱이 릴리즈를 위해 빌드가 되면, 원래의 크기에서 Realm은 오직 1MB 정도만 추가합니다. 현재 배포되고있는 Realm 라이브러리는 iOS와 watchOS 시뮬레이터를 지원하고 디버그 심볼, Bitcode을 포함해 상당히 커지고 있습니다. 앱을 빌드하게 되면 Xcode에 의해 자동적으로 제거됩니다.
상용 어플리케이션에도 Realm을 사용할 수 있나요?
Realm은 2012년부터 상용 제품에 사용되었습니다.
Realm의 Objecitve-C와 Swift API가 커뮤니티의 피드백과 함께 더 많은 기능과 개선 사항으로 개선되고 있다고 기대하셔도 됩니다.
Realm을 사용하기 위해 비용을 지불해야 하나요?
아닙니다. Realm은 상용 프로젝트를 포함하여 사용이 무료입니다.
어떻게 수익을 낼 계획입니까?
이미 기술을 중심으로 기업제품과 서비스를 판매하여 수익을 창출하고 있습니다. 만약 릴리즈나 realm-cocoa보다 현재상태에 대해 더욱 알고 싶으시다면, 이메일로도 대화를 할 수 있습니다. 그리고 공개적인 realm-cocoa를 개발하기 위해, 또한 무료 및 Apache 2.0 라이센스 하에 오픈소스로 유지 될 수 있도록 최선을 다하고 있습니다.
코드 안에 “tightdb”나 “core”로 참조되어 있는 것을 보았습니다. 이것은 무엇인가요?
TightDB는 핵심 C++ 스토리지 엔진의 옛 이름입니다. 그 코어는 현재 오픈 소스가 아니지만 내부를 정리하고 이름을 정리하고 주요 기능을 정하면 역시 Apache 2.0 라이센스로 오픈소스화를 할 계획입니다. 또한, 해당 바이너리는 Realm Core(TightDB)의 바이너리 라이센스 내에서 사용할 수 있습니다.