mirror of
https://github.com/zhigang1992/RxRealm.git
synced 2026-01-12 17:52:24 +08:00
* first commit
This commit is contained in:
9
Example/Podfile
Normal file
9
Example/Podfile
Normal file
@@ -0,0 +1,9 @@
|
||||
source 'https://github.com/CocoaPods/Specs.git'
|
||||
use_frameworks!
|
||||
|
||||
target 'RxRealm_Example', :exclusive => true do
|
||||
platform :ios, '8.3'
|
||||
pod 'RxRealm', :path => '../'
|
||||
pod 'RxSwift'
|
||||
pod 'RealmSwift'
|
||||
end
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMAccessor.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMAccessor.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMAccessor.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMArray.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMArray.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMArray.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMArray_Private.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMArray_Private.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMArray_Private.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMCollection.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMCollection.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMCollection.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMConstants.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMConstants.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMConstants.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMDefines.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMDefines.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMDefines.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMListBase.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMListBase.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMListBase.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMMigration.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMMigration.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMMigration.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMMigration_Private.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMMigration_Private.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMMigration_Private.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMObject.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMObject.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMObject.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMObjectBase.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMObjectBase.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMObjectBase.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMObjectBase_Dynamic.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMObjectBase_Dynamic.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMObjectBase_Dynamic.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMObjectSchema.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMObjectSchema.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMObjectSchema.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMObjectSchema_Private.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMObjectSchema_Private.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMObjectSchema_Private.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMObjectStore.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMObjectStore.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMObjectStore.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMObject_Private.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMObject_Private.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMObject_Private.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMOptionalBase.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMOptionalBase.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMOptionalBase.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMPlatform.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMPlatform.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMPlatform.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMProperty.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMProperty.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMProperty.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMProperty_Private.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMProperty_Private.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMProperty_Private.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMRealm.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMRealm.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMRealm.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMRealmConfiguration.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMRealmConfiguration.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMRealmConfiguration.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMRealmConfiguration_Private.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMRealmConfiguration_Private.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMRealmConfiguration_Private.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMRealm_Dynamic.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMRealm_Dynamic.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMRealm_Dynamic.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMRealm_Private.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMRealm_Private.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMRealm_Private.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMResults.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMResults.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMResults.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMResults_Private.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMResults_Private.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMResults_Private.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMSchema.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMSchema.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMSchema.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/RLMSchema_Private.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/RLMSchema_Private.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/RLMSchema_Private.h
|
||||
1
Example/Pods/Headers/Private/Realm/Realm/Realm.h
generated
Symbolic link
1
Example/Pods/Headers/Private/Realm/Realm/Realm.h
generated
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../Realm/include/Realm/Realm.h
|
||||
35
Example/Pods/Local Podspecs/RxRealm.podspec.json
generated
Normal file
35
Example/Pods/Local Podspecs/RxRealm.podspec.json
generated
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "RxRealm",
|
||||
"version": "0.1.0",
|
||||
"summary": "An Rx wrapper of Realm's collection type",
|
||||
"description": "This is an Rx extension that provides an easy and straight-forward way\nto use Realm's natively reactive collection type as an Observable",
|
||||
"homepage": "https://github.com/RxSwiftCommunity/RxRealm",
|
||||
"license": "MIT",
|
||||
"authors": {
|
||||
"Marin Todorov": "marin@underplot.com"
|
||||
},
|
||||
"source": {
|
||||
"git": "https://github.com/RxSwiftCommunity/RxRealm.git",
|
||||
"tag": "0.1.0"
|
||||
},
|
||||
"requires_arc": true,
|
||||
"platforms": {
|
||||
"ios": "8.3",
|
||||
"osx": "10.10"
|
||||
},
|
||||
"source_files": "Pod/Classes/*.swift",
|
||||
"resource_bundles": {
|
||||
"RxRealm": [
|
||||
"RxRealm/Assets/*.png"
|
||||
]
|
||||
},
|
||||
"frameworks": "Foundation",
|
||||
"dependencies": {
|
||||
"RealmSwift": [
|
||||
|
||||
],
|
||||
"RxSwift": [
|
||||
|
||||
]
|
||||
}
|
||||
}
|
||||
27
Example/Pods/Manifest.lock
generated
Normal file
27
Example/Pods/Manifest.lock
generated
Normal file
@@ -0,0 +1,27 @@
|
||||
PODS:
|
||||
- Realm (0.98.8):
|
||||
- Realm/Headers (= 0.98.8)
|
||||
- Realm/Headers (0.98.8)
|
||||
- RealmSwift (0.98.8):
|
||||
- Realm (= 0.98.8)
|
||||
- RxRealm (0.1.0):
|
||||
- RealmSwift
|
||||
- RxSwift
|
||||
- RxSwift (2.4)
|
||||
|
||||
DEPENDENCIES:
|
||||
- RealmSwift
|
||||
- RxRealm (from `../`)
|
||||
- RxSwift
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
RxRealm:
|
||||
:path: "../"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Realm: 0e293bb62999730599efc3048896bbd4f2e43bcd
|
||||
RealmSwift: 064262d38113f23ff3508fb20a0a922e696bec01
|
||||
RxRealm: 5b53b62a9a973cf62a78b784ee7957dfb0b8dd4e
|
||||
RxSwift: 67b9ef4e8b34fb394e200e754c6a09cc16559f94
|
||||
|
||||
COCOAPODS: 0.39.0
|
||||
8212
Example/Pods/Pods.xcodeproj/project.pbxproj
generated
Normal file
8212
Example/Pods/Pods.xcodeproj/project.pbxproj
generated
Normal file
File diff suppressed because it is too large
Load Diff
60
Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/RxRealm.xcscheme
generated
Normal file
60
Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/RxRealm.xcscheme
generated
Normal file
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0700"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForAnalyzing = "YES"
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = 'primary'
|
||||
BlueprintIdentifier = 'F3CB873952BEC83113DF060C'
|
||||
BlueprintName = 'RxRealm'
|
||||
ReferencedContainer = 'container:Pods.xcodeproj'
|
||||
BuildableName = 'RxRealm.framework'>
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
buildConfiguration = "Debug"
|
||||
allowLocationSimulation = "YES">
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
269
Example/Pods/Realm/LICENSE
generated
Normal file
269
Example/Pods/Realm/LICENSE
generated
Normal file
@@ -0,0 +1,269 @@
|
||||
TABLE OF CONTENTS
|
||||
|
||||
1. Apache License version 2.0
|
||||
2. Realm Components
|
||||
3. Export Compliance
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
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.
|
||||
|
||||
|
||||
REALM COMPONENTS
|
||||
|
||||
This software contains components with separate copyright and license terms.
|
||||
Your use of these components is subject to the terms and conditions of the
|
||||
following licenses.
|
||||
|
||||
For the Realm Core component
|
||||
|
||||
Realm Core Binary License
|
||||
|
||||
Copyright (c) 2011-2014 Realm Inc All rights reserved
|
||||
|
||||
Redistribution and use in binary form, with or without modification, is
|
||||
permitted provided that the following conditions are met:
|
||||
|
||||
1. You agree not to attempt to decompile, disassemble, reverse engineer or
|
||||
otherwise discover the source code from which the binary code was derived.
|
||||
You may, however, access and obtain a separate license for most of the
|
||||
source code from which this Software was created, at
|
||||
http://realm.io/pricing/.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
EXPORT COMPLIANCE
|
||||
|
||||
You understand that the Software may contain cryptographic functions that may be
|
||||
subject to export restrictions, and you represent and warrant that you are not
|
||||
located in a country that is subject to United States export restriction or embargo,
|
||||
including Cuba, Iran, North Korea, Sudan, Syria or the Crimea region, and that you
|
||||
are not on the Department of Commerce list of Denied Persons, Unverified Parties,
|
||||
or affiliated with a Restricted Entity.
|
||||
|
||||
You agree to comply with all export, re-export and import restrictions and
|
||||
regulations of the Department of Commerce or other agency or authority of the
|
||||
United States or other applicable countries. You also agree not to transfer, or
|
||||
authorize the transfer of, directly or indirectly, the Software to any prohibited
|
||||
country, including Cuba, Iran, North Korea, Sudan, Syria or the Crimea region,
|
||||
or to any person or organization on or affiliated with the Department of
|
||||
Commerce lists of Denied Persons, Unverified Parties or Restricted Entities, or
|
||||
otherwise in violation of any such restrictions or regulations.
|
||||
70
Example/Pods/Realm/README.md
generated
Normal file
70
Example/Pods/Realm/README.md
generated
Normal file
@@ -0,0 +1,70 @@
|
||||

|
||||
|
||||
Realm is a mobile database that runs directly inside phones, tablets or wearables.
|
||||
This repository holds the source code for the iOS & OSX versions of Realm, for both Swift & Objective-C.
|
||||
|
||||
## Features
|
||||
|
||||
* **Mobile-first:** Realm is the first database built from the ground up to run directly inside phones, tablets and wearables.
|
||||
* **Simple:** Data is directly [exposed as objects](https://realm.io/docs/objc/latest/#models) and [queryable by code](https://realm.io/docs/objc/latest/#queries), removing the need for ORM's riddled with performance & maintenance issues. Plus, we've worked hard to [keep our API down to just 4 common classes](https://realm.io/docs/objc/latest/api/) (Object, Array, Results and Realms) and 1 utility class (Migrations): most of our users pick it up intuitively, getting simple apps up & running in minutes.
|
||||
* **Modern:** Realm supports relationships, generics, vectorization and even Swift.
|
||||
* **Fast:** Realm is faster than even raw SQLite on common operations, while maintaining an extremely rich feature set.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Please see the detailed instructions in our docs to add [Realm Objective-C](https://realm.io/docs/objc/latest/#installation) _or_ [Realm Swift](https://realm.io/docs/swift/latest/#installation) to your Xcode project.
|
||||
|
||||
## Documentation
|
||||
|
||||
### Realm Objective-C
|
||||
|
||||
The documentation can be found at [realm.io/docs/objc/latest](https://realm.io/docs/objc/latest).
|
||||
The API reference is located at [realm.io/docs/objc/latest/api](https://realm.io/docs/objc/latest/api).
|
||||
|
||||
### Realm Swift
|
||||
|
||||
The documentation can be found at [realm.io/docs/swift/latest](https://realm.io/docs/swift/latest).
|
||||
The API reference is located at [realm.io/docs/swift/latest/api](https://realm.io/docs/swift/latest/api).
|
||||
|
||||
## Getting Help
|
||||
|
||||
- **Need help with your code?**: Look for previous questions on the [#realm tag](https://stackoverflow.com/questions/tagged/realm?sort=newest) — or [ask a new question](https://stackoverflow.com/questions/ask?tags=realm). We activtely monitor & answer questions on SO!
|
||||
- **Have a bug to report?** [Open an issue](https://github.com/realm/realm-cocoa/issues/new). If possible, include the version of Realm, a full log, the Realm file, and a project that shows the issue.
|
||||
- **Have a feature request?** [Open an issue](https://github.com/realm/realm-cocoa/issues/new). Tell us what the feature should do, and why you want the feature.
|
||||
- Sign up for our [**Community Newsletter**](http://eepurl.com/VEKCn) to get regular tips, learn about other use-cases and get alerted of blogposts and tutorials about Realm.
|
||||
|
||||
## Building Realm
|
||||
|
||||
In case you don't want to use the precompiled version, you can build Realm yourself from source.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
* Building Realm requires Xcode 6.4-7.2.
|
||||
* Building Realm documentation requires [jazzy](https://github.com/realm/jazzy)
|
||||
|
||||
Once you have all the necessary prerequisites, building Realm.framework just takes a single command: `sh build.sh build`. You'll need an internet connection the first time you build Realm to download the core binary.
|
||||
|
||||
Run `sh build.sh help` to see all the actions you can perform (build ios/osx, generate docs, test, etc.).
|
||||
|
||||
Executing the examples under the `examples/` folder, requires that you have built the `Realm.framework`.
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md) for more details!
|
||||
|
||||
## License
|
||||
|
||||
Realm Objective-C & Realm Swift are published under the Apache 2.0 license.
|
||||
The underlying core is available under the [Realm Core Binary License](https://github.com/realm/realm-cocoa/blob/master/LICENSE#L210-L243) while we [work to open-source it under the Apache 2.0 license](https://realm.io/docs/objc/latest/#faq).
|
||||
|
||||
**This product is not being made available to any person located in Cuba, Iran,
|
||||
North Korea, Sudan, Syria or the Crimea region, or to any other person that is
|
||||
not eligible to receive the product under U.S. law.**
|
||||
|
||||
## Feedback
|
||||
|
||||
**_If you use Realm and are happy with it, all we ask is that you please consider sending out a tweet mentioning [@realm](https://twitter.com/realm), announce your app on [our mailing-list](https://groups.google.com/forum/#!forum/realm-cocoa), or email [help@realm.io](mailto:help@realm.io) to let us know about it!_**
|
||||
|
||||
**_And if you don't like it, please let us know what you would like improved, so we can fix it!_**
|
||||
|
||||

|
||||
94
Example/Pods/Realm/Realm/ObjectStore/impl/apple/cached_realm.cpp
generated
Normal file
94
Example/Pods/Realm/Realm/ObjectStore/impl/apple/cached_realm.cpp
generated
Normal file
@@ -0,0 +1,94 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2015 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.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "impl/cached_realm.hpp"
|
||||
|
||||
#include "shared_realm.hpp"
|
||||
|
||||
using namespace realm;
|
||||
using namespace realm::_impl;
|
||||
|
||||
CachedRealm::CachedRealm(const std::shared_ptr<Realm>& realm, bool cache)
|
||||
: CachedRealmBase(realm, cache)
|
||||
{
|
||||
struct RefCountedWeakPointer {
|
||||
std::weak_ptr<Realm> realm;
|
||||
std::atomic<size_t> ref_count = {1};
|
||||
};
|
||||
|
||||
CFRunLoopSourceContext ctx{};
|
||||
ctx.info = new RefCountedWeakPointer{realm};
|
||||
ctx.perform = [](void* info) {
|
||||
if (auto realm = static_cast<RefCountedWeakPointer*>(info)->realm.lock()) {
|
||||
realm->notify();
|
||||
}
|
||||
};
|
||||
ctx.retain = [](const void* info) {
|
||||
static_cast<RefCountedWeakPointer*>(const_cast<void*>(info))->ref_count.fetch_add(1, std::memory_order_relaxed);
|
||||
return info;
|
||||
};
|
||||
ctx.release = [](const void* info) {
|
||||
auto ptr = static_cast<RefCountedWeakPointer*>(const_cast<void*>(info));
|
||||
if (ptr->ref_count.fetch_add(-1, std::memory_order_acq_rel) == 1) {
|
||||
delete ptr;
|
||||
}
|
||||
};
|
||||
|
||||
m_runloop = CFRunLoopGetCurrent();
|
||||
CFRetain(m_runloop);
|
||||
m_signal = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &ctx);
|
||||
CFRunLoopAddSource(m_runloop, m_signal, kCFRunLoopDefaultMode);
|
||||
}
|
||||
|
||||
CachedRealm::CachedRealm(CachedRealm&& rgt)
|
||||
: CachedRealmBase(std::move(rgt))
|
||||
, m_runloop(rgt.m_runloop)
|
||||
, m_signal(rgt.m_signal)
|
||||
{
|
||||
rgt.m_runloop = nullptr;
|
||||
rgt.m_signal = nullptr;
|
||||
}
|
||||
|
||||
CachedRealm& CachedRealm::operator=(CachedRealm&& rgt)
|
||||
{
|
||||
CachedRealmBase::operator=(std::move(rgt));
|
||||
m_runloop = rgt.m_runloop;
|
||||
m_signal = rgt.m_signal;
|
||||
rgt.m_runloop = nullptr;
|
||||
rgt.m_signal = nullptr;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CachedRealm::~CachedRealm()
|
||||
{
|
||||
if (m_signal) {
|
||||
CFRunLoopSourceInvalidate(m_signal);
|
||||
CFRelease(m_signal);
|
||||
CFRelease(m_runloop);
|
||||
}
|
||||
}
|
||||
|
||||
void CachedRealm::notify()
|
||||
{
|
||||
CFRunLoopSourceSignal(m_signal);
|
||||
// Signalling the source makes it run the next time the runloop gets
|
||||
// to it, but doesn't make the runloop start if it's currently idle
|
||||
// waiting for events
|
||||
CFRunLoopWakeUp(m_runloop);
|
||||
}
|
||||
226
Example/Pods/Realm/Realm/ObjectStore/impl/apple/external_commit_helper.cpp
generated
Normal file
226
Example/Pods/Realm/Realm/ObjectStore/impl/apple/external_commit_helper.cpp
generated
Normal file
@@ -0,0 +1,226 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2015 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.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "impl/external_commit_helper.hpp"
|
||||
|
||||
#include "impl/realm_coordinator.hpp"
|
||||
|
||||
#include <asl.h>
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <sstream>
|
||||
#include <sys/event.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <system_error>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace realm;
|
||||
using namespace realm::_impl;
|
||||
|
||||
namespace {
|
||||
// Write a byte to a pipe to notify anyone waiting for data on the pipe
|
||||
void notify_fd(int fd)
|
||||
{
|
||||
while (true) {
|
||||
char c = 0;
|
||||
ssize_t ret = write(fd, &c, 1);
|
||||
if (ret == 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
// If the pipe's buffer is full, we need to read some of the old data in
|
||||
// it to make space. We don't just read in the code waiting for
|
||||
// notifications so that we can notify multiple waiters with a single
|
||||
// write.
|
||||
assert(ret == -1 && errno == EAGAIN);
|
||||
char buff[1024];
|
||||
read(fd, buff, sizeof buff);
|
||||
}
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
void ExternalCommitHelper::FdHolder::close()
|
||||
{
|
||||
if (m_fd != -1) {
|
||||
::close(m_fd);
|
||||
}
|
||||
m_fd = -1;
|
||||
}
|
||||
|
||||
// Inter-thread and inter-process notifications of changes are done using a
|
||||
// named pipe in the filesystem next to the Realm file. Everyone who wants to be
|
||||
// notified of commits waits for data to become available on the pipe, and anyone
|
||||
// who commits a write transaction writes data to the pipe after releasing the
|
||||
// write lock. Note that no one ever actually *reads* from the pipe: the data
|
||||
// actually written is meaningless, and trying to read from a pipe from multiple
|
||||
// processes at once is fraught with race conditions.
|
||||
|
||||
// When a RLMRealm instance is created, we add a CFRunLoopSource to the current
|
||||
// thread's runloop. On each cycle of the run loop, the run loop checks each of
|
||||
// its sources for work to do, which in the case of CFRunLoopSource is just
|
||||
// checking if CFRunLoopSourceSignal has been called since the last time it ran,
|
||||
// and if so invokes the function pointer supplied when the source is created,
|
||||
// which in our case just invokes `[realm handleExternalChange]`.
|
||||
|
||||
// Listening for external changes is done using kqueue() on a background thread.
|
||||
// kqueue() lets us efficiently wait until the amount of data which can be read
|
||||
// from one or more file descriptors has changed, and tells us which of the file
|
||||
// descriptors it was that changed. We use this to wait on both the shared named
|
||||
// pipe, and a local anonymous pipe. When data is written to the named pipe, we
|
||||
// signal the runloop source and wake up the target runloop, and when data is
|
||||
// written to the anonymous pipe the background thread removes the runloop
|
||||
// source from the runloop and and shuts down.
|
||||
ExternalCommitHelper::ExternalCommitHelper(RealmCoordinator& parent)
|
||||
: m_parent(parent)
|
||||
{
|
||||
m_kq = kqueue();
|
||||
if (m_kq == -1) {
|
||||
throw std::system_error(errno, std::system_category());
|
||||
}
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
auto path = parent.get_path() + ".note";
|
||||
|
||||
// Create and open the named pipe
|
||||
int ret = mkfifo(path.c_str(), 0600);
|
||||
if (ret == -1) {
|
||||
int err = errno;
|
||||
if (err == ENOTSUP) {
|
||||
// Filesystem doesn't support named pipes, so try putting it in tmp instead
|
||||
// Hash collisions are okay here because they just result in doing
|
||||
// extra work, as opposed to correctness problems
|
||||
std::ostringstream ss;
|
||||
ss << getenv("TMPDIR");
|
||||
ss << "realm_" << std::hash<std::string>()(path) << ".note";
|
||||
path = ss.str();
|
||||
ret = mkfifo(path.c_str(), 0600);
|
||||
err = errno;
|
||||
}
|
||||
// the fifo already existing isn't an error
|
||||
if (ret == -1 && err != EEXIST) {
|
||||
throw std::system_error(err, std::system_category());
|
||||
}
|
||||
}
|
||||
|
||||
m_notify_fd = open(path.c_str(), O_RDWR);
|
||||
if (m_notify_fd == -1) {
|
||||
throw std::system_error(errno, std::system_category());
|
||||
}
|
||||
|
||||
// Make writing to the pipe return -1 when the pipe's buffer is full
|
||||
// rather than blocking until there's space available
|
||||
ret = fcntl(m_notify_fd, F_SETFL, O_NONBLOCK);
|
||||
if (ret == -1) {
|
||||
throw std::system_error(errno, std::system_category());
|
||||
}
|
||||
|
||||
#else // !TARGET_OS_TV
|
||||
|
||||
// tvOS does not support named pipes, so use an anonymous pipe instead
|
||||
int notification_pipe[2];
|
||||
int ret = pipe(notification_pipe);
|
||||
if (ret == -1) {
|
||||
throw std::system_error(errno, std::system_category());
|
||||
}
|
||||
|
||||
m_notify_fd = notification_pipe[0];
|
||||
m_notify_fd_write = notification_pipe[1];
|
||||
|
||||
#endif // TARGET_OS_TV
|
||||
|
||||
// Create the anonymous pipe for shutdown notifications
|
||||
int shutdown_pipe[2];
|
||||
ret = pipe(shutdown_pipe);
|
||||
if (ret == -1) {
|
||||
throw std::system_error(errno, std::system_category());
|
||||
}
|
||||
|
||||
m_shutdown_read_fd = shutdown_pipe[0];
|
||||
m_shutdown_write_fd = shutdown_pipe[1];
|
||||
|
||||
m_thread = std::async(std::launch::async, [=] {
|
||||
try {
|
||||
listen();
|
||||
}
|
||||
catch (std::exception const& e) {
|
||||
fprintf(stderr, "uncaught exception in notifier thread: %s: %s\n", typeid(e).name(), e.what());
|
||||
asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "uncaught exception in notifier thread: %s: %s", typeid(e).name(), e.what());
|
||||
throw;
|
||||
}
|
||||
catch (...) {
|
||||
fprintf(stderr, "uncaught exception in notifier thread\n");
|
||||
asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "uncaught exception in notifier thread");
|
||||
throw;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ExternalCommitHelper::~ExternalCommitHelper()
|
||||
{
|
||||
notify_fd(m_shutdown_write_fd);
|
||||
m_thread.wait(); // Wait for the thread to exit
|
||||
}
|
||||
|
||||
void ExternalCommitHelper::listen()
|
||||
{
|
||||
pthread_setname_np("RLMRealm notification listener");
|
||||
|
||||
// Set up the kqueue
|
||||
// EVFILT_READ indicates that we care about data being available to read
|
||||
// on the given file descriptor.
|
||||
// EV_CLEAR makes it wait for the amount of data available to be read to
|
||||
// change rather than just returning when there is any data to read.
|
||||
struct kevent ke[2];
|
||||
EV_SET(&ke[0], m_notify_fd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, 0);
|
||||
EV_SET(&ke[1], m_shutdown_read_fd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, 0);
|
||||
int ret = kevent(m_kq, ke, 2, nullptr, 0, nullptr);
|
||||
assert(ret == 0);
|
||||
|
||||
while (true) {
|
||||
struct kevent event;
|
||||
// Wait for data to become on either fd
|
||||
// Return code is number of bytes available or -1 on error
|
||||
ret = kevent(m_kq, nullptr, 0, &event, 1, nullptr);
|
||||
assert(ret >= 0);
|
||||
if (ret == 0) {
|
||||
// Spurious wakeup; just wait again
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check which file descriptor had activity: if it's the shutdown
|
||||
// pipe, then someone called -stop; otherwise it's the named pipe
|
||||
// and someone committed a write transaction
|
||||
if (event.ident == (uint32_t)m_shutdown_read_fd) {
|
||||
return;
|
||||
}
|
||||
assert(event.ident == (uint32_t)m_notify_fd);
|
||||
|
||||
m_parent.on_change();
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalCommitHelper::notify_others()
|
||||
{
|
||||
if (m_notify_fd_write != -1) {
|
||||
notify_fd(m_notify_fd_write);
|
||||
}
|
||||
else {
|
||||
notify_fd(m_notify_fd);
|
||||
}
|
||||
}
|
||||
290
Example/Pods/Realm/Realm/ObjectStore/impl/async_query.cpp
generated
Normal file
290
Example/Pods/Realm/Realm/ObjectStore/impl/async_query.cpp
generated
Normal file
@@ -0,0 +1,290 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2015 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.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "impl/async_query.hpp"
|
||||
|
||||
#include "impl/realm_coordinator.hpp"
|
||||
#include "results.hpp"
|
||||
|
||||
using namespace realm;
|
||||
using namespace realm::_impl;
|
||||
|
||||
AsyncQuery::AsyncQuery(Results& target)
|
||||
: m_target_results(&target)
|
||||
, m_realm(target.get_realm().shared_from_this())
|
||||
, m_sort(target.get_sort())
|
||||
, m_sg_version(Realm::Internal::get_shared_group(*m_realm).get_version_of_current_transaction())
|
||||
{
|
||||
Query q = target.get_query();
|
||||
m_query_handover = Realm::Internal::get_shared_group(*m_realm).export_for_handover(q, MutableSourcePayload::Move);
|
||||
}
|
||||
|
||||
AsyncQuery::~AsyncQuery()
|
||||
{
|
||||
// unregister() may have been called from a different thread than we're being
|
||||
// destroyed on, so we need to synchronize access to the interesting fields
|
||||
// modified there
|
||||
std::lock_guard<std::mutex> lock(m_target_mutex);
|
||||
m_realm = nullptr;
|
||||
}
|
||||
|
||||
size_t AsyncQuery::add_callback(std::function<void (std::exception_ptr)> callback)
|
||||
{
|
||||
m_realm->verify_thread();
|
||||
|
||||
auto next_token = [=] {
|
||||
size_t token = 0;
|
||||
for (auto& callback : m_callbacks) {
|
||||
if (token <= callback.token) {
|
||||
token = callback.token + 1;
|
||||
}
|
||||
}
|
||||
return token;
|
||||
};
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_callback_mutex);
|
||||
auto token = next_token();
|
||||
m_callbacks.push_back({std::move(callback), token, -1ULL});
|
||||
if (m_callback_index == npos) { // Don't need to wake up if we're already sending notifications
|
||||
Realm::Internal::get_coordinator(*m_realm).send_commit_notifications();
|
||||
m_have_callbacks = true;
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
void AsyncQuery::remove_callback(size_t token)
|
||||
{
|
||||
Callback old;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_callback_mutex);
|
||||
REALM_ASSERT(m_error || m_callbacks.size() > 0);
|
||||
|
||||
auto it = find_if(begin(m_callbacks), end(m_callbacks),
|
||||
[=](const auto& c) { return c.token == token; });
|
||||
// We should only fail to find the callback if it was removed due to an error
|
||||
REALM_ASSERT(m_error || it != end(m_callbacks));
|
||||
if (it == end(m_callbacks)) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t idx = distance(begin(m_callbacks), it);
|
||||
if (m_callback_index != npos && m_callback_index >= idx) {
|
||||
--m_callback_index;
|
||||
}
|
||||
|
||||
old = std::move(*it);
|
||||
m_callbacks.erase(it);
|
||||
|
||||
m_have_callbacks = !m_callbacks.empty();
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncQuery::unregister() noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_target_mutex);
|
||||
m_target_results = nullptr;
|
||||
m_realm = nullptr;
|
||||
}
|
||||
|
||||
void AsyncQuery::release_query() noexcept
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_target_mutex);
|
||||
REALM_ASSERT(!m_realm && !m_target_results);
|
||||
}
|
||||
|
||||
m_query = nullptr;
|
||||
}
|
||||
|
||||
bool AsyncQuery::is_alive() const noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_target_mutex);
|
||||
return m_target_results != nullptr;
|
||||
}
|
||||
|
||||
// Most of the inter-thread synchronization for run(), prepare_handover(),
|
||||
// attach_to(), detach(), release_query() and deliver() is done by
|
||||
// RealmCoordinator external to this code, which has some potentially
|
||||
// non-obvious results on which members are and are not safe to use without
|
||||
// holding a lock.
|
||||
//
|
||||
// attach_to(), detach(), run(), prepare_handover(), and release_query() are
|
||||
// all only ever called on a single thread. call_callbacks() and deliver() are
|
||||
// called on the same thread. Calls to prepare_handover() and deliver() are
|
||||
// guarded by a lock.
|
||||
//
|
||||
// In total, this means that the safe data flow is as follows:
|
||||
// - prepare_handover(), attach_to(), detach() and release_query() can read
|
||||
// members written by each other
|
||||
// - deliver() can read members written to in prepare_handover(), deliver(),
|
||||
// and call_callbacks()
|
||||
// - call_callbacks() and read members written to in deliver()
|
||||
//
|
||||
// Separately from this data flow for the query results, all uses of
|
||||
// m_target_results, m_callbacks, and m_callback_index must be done with the
|
||||
// appropriate mutex held to avoid race conditions when the Results object is
|
||||
// destroyed while the background work is running, and to allow removing
|
||||
// callbacks from any thread.
|
||||
|
||||
void AsyncQuery::run()
|
||||
{
|
||||
REALM_ASSERT(m_sg);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> target_lock(m_target_mutex);
|
||||
// Don't run the query if the results aren't actually going to be used
|
||||
if (!m_target_results || (!m_have_callbacks && !m_target_results->wants_background_updates())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
REALM_ASSERT(!m_tv.is_attached());
|
||||
|
||||
// If we've run previously, check if we need to rerun
|
||||
if (m_initial_run_complete) {
|
||||
// Make an empty tableview from the query to get the table version, since
|
||||
// Query doesn't expose it
|
||||
if (m_query->find_all(0, 0, 0).sync_if_needed() == m_handed_over_table_version) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_tv = m_query->find_all();
|
||||
if (m_sort) {
|
||||
m_tv.sort(m_sort.columnIndices, m_sort.ascending);
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncQuery::prepare_handover()
|
||||
{
|
||||
m_sg_version = m_sg->get_version_of_current_transaction();
|
||||
|
||||
if (!m_tv.is_attached()) {
|
||||
return;
|
||||
}
|
||||
|
||||
REALM_ASSERT(m_tv.is_in_sync());
|
||||
|
||||
m_initial_run_complete = true;
|
||||
m_handed_over_table_version = m_tv.sync_if_needed();
|
||||
m_tv_handover = m_sg->export_for_handover(m_tv, MutableSourcePayload::Move);
|
||||
|
||||
// detach the TableView as we won't need it again and keeping it around
|
||||
// makes advance_read() much more expensive
|
||||
m_tv = TableView();
|
||||
}
|
||||
|
||||
bool AsyncQuery::deliver(SharedGroup& sg, std::exception_ptr err)
|
||||
{
|
||||
if (!is_for_current_thread()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> target_lock(m_target_mutex);
|
||||
|
||||
// Target results being null here indicates that it was destroyed while we
|
||||
// were in the process of advancing the Realm version and preparing for
|
||||
// delivery, i.e. it was destroyed from the "wrong" thread
|
||||
if (!m_target_results) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We can get called before the query has actually had the chance to run if
|
||||
// we're added immediately before a different set of async results are
|
||||
// delivered
|
||||
if (!m_initial_run_complete && !err) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
m_error = err;
|
||||
return m_have_callbacks;
|
||||
}
|
||||
|
||||
REALM_ASSERT(!m_query_handover);
|
||||
|
||||
auto realm_sg_version = Realm::Internal::get_shared_group(*m_realm).get_version_of_current_transaction();
|
||||
if (m_sg_version != realm_sg_version) {
|
||||
// Realm version can be newer if a commit was made on our thread or the
|
||||
// user manually called refresh(), or older if a commit was made on a
|
||||
// different thread and we ran *really* fast in between the check for
|
||||
// if the shared group has changed and when we pick up async results
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_tv_handover) {
|
||||
m_tv_handover->version = m_sg_version;
|
||||
Results::Internal::set_table_view(*m_target_results,
|
||||
std::move(*sg.import_from_handover(std::move(m_tv_handover))));
|
||||
m_delivered_table_version = m_handed_over_table_version;
|
||||
|
||||
}
|
||||
REALM_ASSERT(!m_tv_handover);
|
||||
return m_have_callbacks;
|
||||
}
|
||||
|
||||
void AsyncQuery::call_callbacks()
|
||||
{
|
||||
REALM_ASSERT(is_for_current_thread());
|
||||
|
||||
while (auto fn = next_callback()) {
|
||||
fn(m_error);
|
||||
}
|
||||
|
||||
if (m_error) {
|
||||
// Remove all the callbacks as we never need to call anything ever again
|
||||
// after delivering an error
|
||||
std::lock_guard<std::mutex> callback_lock(m_callback_mutex);
|
||||
m_callbacks.clear();
|
||||
}
|
||||
}
|
||||
|
||||
std::function<void (std::exception_ptr)> AsyncQuery::next_callback()
|
||||
{
|
||||
std::lock_guard<std::mutex> callback_lock(m_callback_mutex);
|
||||
for (++m_callback_index; m_callback_index < m_callbacks.size(); ++m_callback_index) {
|
||||
auto& callback = m_callbacks[m_callback_index];
|
||||
if (m_error || callback.delivered_version != m_delivered_table_version) {
|
||||
callback.delivered_version = m_delivered_table_version;
|
||||
return callback.fn;
|
||||
}
|
||||
}
|
||||
|
||||
m_callback_index = npos;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AsyncQuery::attach_to(realm::SharedGroup& sg)
|
||||
{
|
||||
REALM_ASSERT(!m_sg);
|
||||
REALM_ASSERT(m_query_handover);
|
||||
|
||||
m_query = sg.import_from_handover(std::move(m_query_handover));
|
||||
m_sg = &sg;
|
||||
}
|
||||
|
||||
void AsyncQuery::detatch()
|
||||
{
|
||||
REALM_ASSERT(m_sg);
|
||||
REALM_ASSERT(m_query);
|
||||
REALM_ASSERT(!m_tv.is_attached());
|
||||
|
||||
m_query_handover = m_sg->export_for_handover(*m_query, MutableSourcePayload::Move);
|
||||
m_sg = nullptr;
|
||||
m_query = nullptr;
|
||||
}
|
||||
480
Example/Pods/Realm/Realm/ObjectStore/impl/realm_coordinator.cpp
generated
Normal file
480
Example/Pods/Realm/Realm/ObjectStore/impl/realm_coordinator.cpp
generated
Normal file
@@ -0,0 +1,480 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2015 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.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "impl/realm_coordinator.hpp"
|
||||
|
||||
#include "impl/async_query.hpp"
|
||||
#include "impl/cached_realm.hpp"
|
||||
#include "impl/external_commit_helper.hpp"
|
||||
#include "impl/transact_log_handler.hpp"
|
||||
#include "object_store.hpp"
|
||||
#include "schema.hpp"
|
||||
|
||||
#include <realm/commit_log.hpp>
|
||||
#include <realm/group_shared.hpp>
|
||||
#include <realm/lang_bind_helper.hpp>
|
||||
#include <realm/query.hpp>
|
||||
#include <realm/table_view.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
using namespace realm;
|
||||
using namespace realm::_impl;
|
||||
|
||||
static std::mutex s_coordinator_mutex;
|
||||
static std::unordered_map<std::string, std::weak_ptr<RealmCoordinator>> s_coordinators_per_path;
|
||||
|
||||
std::shared_ptr<RealmCoordinator> RealmCoordinator::get_coordinator(StringData path)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(s_coordinator_mutex);
|
||||
|
||||
auto& weak_coordinator = s_coordinators_per_path[path];
|
||||
if (auto coordinator = weak_coordinator.lock()) {
|
||||
return coordinator;
|
||||
}
|
||||
|
||||
auto coordinator = std::make_shared<RealmCoordinator>();
|
||||
weak_coordinator = coordinator;
|
||||
return coordinator;
|
||||
}
|
||||
|
||||
std::shared_ptr<RealmCoordinator> RealmCoordinator::get_existing_coordinator(StringData path)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(s_coordinator_mutex);
|
||||
auto it = s_coordinators_per_path.find(path);
|
||||
return it == s_coordinators_per_path.end() ? nullptr : it->second.lock();
|
||||
}
|
||||
|
||||
std::shared_ptr<Realm> RealmCoordinator::get_realm(Realm::Config config)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_realm_mutex);
|
||||
if ((!m_config.read_only && !m_notifier) || (m_config.read_only && m_cached_realms.empty())) {
|
||||
m_config = config;
|
||||
if (!config.read_only && !m_notifier) {
|
||||
try {
|
||||
m_notifier = std::make_unique<ExternalCommitHelper>(*this);
|
||||
}
|
||||
catch (std::system_error const& ex) {
|
||||
throw RealmFileException(RealmFileException::Kind::AccessError, config.path, ex.code().message());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (m_config.read_only != config.read_only) {
|
||||
throw MismatchedConfigException("Realm at path already opened with different read permissions.");
|
||||
}
|
||||
if (m_config.in_memory != config.in_memory) {
|
||||
throw MismatchedConfigException("Realm at path already opened with different inMemory settings.");
|
||||
}
|
||||
if (m_config.encryption_key != config.encryption_key) {
|
||||
throw MismatchedConfigException("Realm at path already opened with a different encryption key.");
|
||||
}
|
||||
if (m_config.schema_version != config.schema_version && config.schema_version != ObjectStore::NotVersioned) {
|
||||
throw MismatchedConfigException("Realm at path already opened with different schema version.");
|
||||
}
|
||||
// FIXME: verify that schema is compatible
|
||||
// Needs to verify that all tables present in both are identical, and
|
||||
// then updated m_config with any tables present in config but not in
|
||||
// it
|
||||
// Public API currently doesn't make it possible to have non-matching
|
||||
// schemata so it's not a huge issue
|
||||
if ((false) && m_config.schema != config.schema) {
|
||||
throw MismatchedConfigException("Realm at path already opened with different schema");
|
||||
}
|
||||
}
|
||||
|
||||
if (config.cache) {
|
||||
for (auto& cachedRealm : m_cached_realms) {
|
||||
if (cachedRealm.is_cached_for_current_thread()) {
|
||||
// can be null if we jumped in between ref count hitting zero and
|
||||
// unregister_realm() getting the lock
|
||||
if (auto realm = cachedRealm.realm()) {
|
||||
return realm;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto realm = std::make_shared<Realm>(std::move(config));
|
||||
realm->init(shared_from_this());
|
||||
m_cached_realms.emplace_back(realm, m_config.cache);
|
||||
return realm;
|
||||
}
|
||||
|
||||
std::shared_ptr<Realm> RealmCoordinator::get_realm()
|
||||
{
|
||||
return get_realm(m_config);
|
||||
}
|
||||
|
||||
const Schema* RealmCoordinator::get_schema() const noexcept
|
||||
{
|
||||
return m_cached_realms.empty() ? nullptr : m_config.schema.get();
|
||||
}
|
||||
|
||||
void RealmCoordinator::update_schema(Schema const& schema)
|
||||
{
|
||||
// FIXME: this should probably be doing some sort of validation and
|
||||
// notifying all Realm instances of the new schema in some way
|
||||
m_config.schema = std::make_unique<Schema>(schema);
|
||||
}
|
||||
|
||||
RealmCoordinator::RealmCoordinator() = default;
|
||||
|
||||
RealmCoordinator::~RealmCoordinator()
|
||||
{
|
||||
std::lock_guard<std::mutex> coordinator_lock(s_coordinator_mutex);
|
||||
for (auto it = s_coordinators_per_path.begin(); it != s_coordinators_per_path.end(); ) {
|
||||
if (it->second.expired()) {
|
||||
it = s_coordinators_per_path.erase(it);
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RealmCoordinator::unregister_realm(Realm* realm)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_realm_mutex);
|
||||
for (size_t i = 0; i < m_cached_realms.size(); ++i) {
|
||||
auto& cached_realm = m_cached_realms[i];
|
||||
if (!cached_realm.expired() && !cached_realm.is_for_realm(realm)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i + 1 < m_cached_realms.size()) {
|
||||
cached_realm = std::move(m_cached_realms.back());
|
||||
}
|
||||
m_cached_realms.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void RealmCoordinator::clear_cache()
|
||||
{
|
||||
std::vector<WeakRealm> realms_to_close;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(s_coordinator_mutex);
|
||||
|
||||
for (auto& weak_coordinator : s_coordinators_per_path) {
|
||||
auto coordinator = weak_coordinator.second.lock();
|
||||
if (!coordinator) {
|
||||
continue;
|
||||
}
|
||||
|
||||
coordinator->m_notifier = nullptr;
|
||||
|
||||
// Gather a list of all of the realms which will be removed
|
||||
for (auto& cached_realm : coordinator->m_cached_realms) {
|
||||
if (auto realm = cached_realm.realm()) {
|
||||
realms_to_close.push_back(realm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s_coordinators_per_path.clear();
|
||||
}
|
||||
|
||||
// Close all of the previously cached Realms. This can't be done while
|
||||
// s_coordinator_mutex is held as it may try to re-lock it.
|
||||
for (auto& weak_realm : realms_to_close) {
|
||||
if (auto realm = weak_realm.lock()) {
|
||||
realm->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RealmCoordinator::send_commit_notifications()
|
||||
{
|
||||
REALM_ASSERT(!m_config.read_only);
|
||||
m_notifier->notify_others();
|
||||
}
|
||||
|
||||
void RealmCoordinator::pin_version(uint_fast64_t version, uint_fast32_t index)
|
||||
{
|
||||
if (m_async_error) {
|
||||
return;
|
||||
}
|
||||
|
||||
SharedGroup::VersionID versionid(version, index);
|
||||
if (!m_advancer_sg) {
|
||||
try {
|
||||
std::unique_ptr<Group> read_only_group;
|
||||
Realm::open_with_config(m_config, m_advancer_history, m_advancer_sg, read_only_group);
|
||||
REALM_ASSERT(!read_only_group);
|
||||
m_advancer_sg->begin_read(versionid);
|
||||
}
|
||||
catch (...) {
|
||||
m_async_error = std::current_exception();
|
||||
m_advancer_sg = nullptr;
|
||||
m_advancer_history = nullptr;
|
||||
}
|
||||
}
|
||||
else if (m_new_queries.empty()) {
|
||||
// If this is the first query then we don't already have a read transaction
|
||||
m_advancer_sg->begin_read(versionid);
|
||||
}
|
||||
else if (versionid < m_advancer_sg->get_version_of_current_transaction()) {
|
||||
// Ensure we're holding a readlock on the oldest version we have a
|
||||
// handover object for, as handover objects don't
|
||||
m_advancer_sg->end_read();
|
||||
m_advancer_sg->begin_read(versionid);
|
||||
}
|
||||
}
|
||||
|
||||
void RealmCoordinator::register_query(std::shared_ptr<AsyncQuery> query)
|
||||
{
|
||||
auto version = query->version();
|
||||
auto& self = Realm::Internal::get_coordinator(query->get_realm());
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(self.m_query_mutex);
|
||||
self.pin_version(version.version, version.index);
|
||||
self.m_new_queries.push_back(std::move(query));
|
||||
}
|
||||
}
|
||||
|
||||
void RealmCoordinator::clean_up_dead_queries()
|
||||
{
|
||||
auto swap_remove = [&](auto& container) {
|
||||
bool did_remove = false;
|
||||
for (size_t i = 0; i < container.size(); ++i) {
|
||||
if (container[i]->is_alive())
|
||||
continue;
|
||||
|
||||
// Ensure the query is destroyed here even if there's lingering refs
|
||||
// to the async query elsewhere
|
||||
container[i]->release_query();
|
||||
|
||||
if (container.size() > i + 1)
|
||||
container[i] = std::move(container.back());
|
||||
container.pop_back();
|
||||
--i;
|
||||
did_remove = true;
|
||||
}
|
||||
return did_remove;
|
||||
};
|
||||
|
||||
if (swap_remove(m_queries)) {
|
||||
// Make sure we aren't holding on to read versions needlessly if there
|
||||
// are no queries left, but don't close them entirely as opening shared
|
||||
// groups is expensive
|
||||
if (m_queries.empty() && m_query_sg) {
|
||||
m_query_sg->end_read();
|
||||
}
|
||||
}
|
||||
if (swap_remove(m_new_queries)) {
|
||||
if (m_new_queries.empty() && m_advancer_sg) {
|
||||
m_advancer_sg->end_read();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RealmCoordinator::on_change()
|
||||
{
|
||||
run_async_queries();
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_realm_mutex);
|
||||
for (auto& realm : m_cached_realms) {
|
||||
realm.notify();
|
||||
}
|
||||
}
|
||||
|
||||
void RealmCoordinator::run_async_queries()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_query_mutex);
|
||||
|
||||
clean_up_dead_queries();
|
||||
|
||||
if (m_queries.empty() && m_new_queries.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_async_error) {
|
||||
open_helper_shared_group();
|
||||
}
|
||||
|
||||
if (m_async_error) {
|
||||
move_new_queries_to_main();
|
||||
return;
|
||||
}
|
||||
|
||||
advance_helper_shared_group_to_latest();
|
||||
|
||||
// Make a copy of the queries vector so that we can release the lock while
|
||||
// we run the queries
|
||||
auto queries_to_run = m_queries;
|
||||
lock.unlock();
|
||||
|
||||
for (auto& query : queries_to_run) {
|
||||
query->run();
|
||||
}
|
||||
|
||||
// Reacquire the lock while updating the fields that are actually read on
|
||||
// other threads
|
||||
{
|
||||
lock.lock();
|
||||
for (auto& query : queries_to_run) {
|
||||
query->prepare_handover();
|
||||
}
|
||||
}
|
||||
|
||||
clean_up_dead_queries();
|
||||
}
|
||||
|
||||
void RealmCoordinator::open_helper_shared_group()
|
||||
{
|
||||
if (!m_query_sg) {
|
||||
try {
|
||||
std::unique_ptr<Group> read_only_group;
|
||||
Realm::open_with_config(m_config, m_query_history, m_query_sg, read_only_group);
|
||||
REALM_ASSERT(!read_only_group);
|
||||
m_query_sg->begin_read();
|
||||
}
|
||||
catch (...) {
|
||||
// Store the error to be passed to the async queries
|
||||
m_async_error = std::current_exception();
|
||||
m_query_sg = nullptr;
|
||||
m_query_history = nullptr;
|
||||
}
|
||||
}
|
||||
else if (m_queries.empty()) {
|
||||
m_query_sg->begin_read();
|
||||
}
|
||||
}
|
||||
|
||||
void RealmCoordinator::move_new_queries_to_main()
|
||||
{
|
||||
m_queries.reserve(m_queries.size() + m_new_queries.size());
|
||||
std::move(m_new_queries.begin(), m_new_queries.end(), std::back_inserter(m_queries));
|
||||
m_new_queries.clear();
|
||||
}
|
||||
|
||||
void RealmCoordinator::advance_helper_shared_group_to_latest()
|
||||
{
|
||||
if (m_new_queries.empty()) {
|
||||
LangBindHelper::advance_read(*m_query_sg);
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort newly added queries by their source version so that we can pull them
|
||||
// all forward to the latest version in a single pass over the transaction log
|
||||
std::sort(m_new_queries.begin(), m_new_queries.end(), [](auto const& lft, auto const& rgt) {
|
||||
return lft->version() < rgt->version();
|
||||
});
|
||||
|
||||
// Import all newly added queries to our helper SG
|
||||
for (auto& query : m_new_queries) {
|
||||
LangBindHelper::advance_read(*m_advancer_sg, query->version());
|
||||
query->attach_to(*m_advancer_sg);
|
||||
}
|
||||
|
||||
// Advance both SGs to the newest version
|
||||
LangBindHelper::advance_read(*m_advancer_sg);
|
||||
LangBindHelper::advance_read(*m_query_sg, m_advancer_sg->get_version_of_current_transaction());
|
||||
|
||||
// Transfer all new queries over to the main SG
|
||||
for (auto& query : m_new_queries) {
|
||||
query->detatch();
|
||||
query->attach_to(*m_query_sg);
|
||||
}
|
||||
|
||||
move_new_queries_to_main();
|
||||
m_advancer_sg->end_read();
|
||||
}
|
||||
|
||||
void RealmCoordinator::advance_to_ready(Realm& realm)
|
||||
{
|
||||
decltype(m_queries) queries;
|
||||
|
||||
auto& sg = Realm::Internal::get_shared_group(realm);
|
||||
|
||||
auto get_query_version = [&] {
|
||||
for (auto& query : m_queries) {
|
||||
auto version = query->version();
|
||||
if (version != SharedGroup::VersionID{}) {
|
||||
return version;
|
||||
}
|
||||
}
|
||||
return SharedGroup::VersionID{};
|
||||
};
|
||||
|
||||
SharedGroup::VersionID version;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_query_mutex);
|
||||
version = get_query_version();
|
||||
}
|
||||
|
||||
// no async queries; just advance to latest
|
||||
if (version.version == std::numeric_limits<uint_fast64_t>::max()) {
|
||||
transaction::advance(sg, realm.m_binding_context.get());
|
||||
return;
|
||||
}
|
||||
|
||||
// async results are out of date; ignore
|
||||
if (version < sg.get_version_of_current_transaction()) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
// Advance to the ready version without holding any locks because it
|
||||
// may end up calling user code (in did_change() notifications)
|
||||
transaction::advance(sg, realm.m_binding_context.get(), version);
|
||||
|
||||
// Reacquire the lock and recheck the query version, as the queries may
|
||||
// have advanced to a later version while we didn't hold the lock. If
|
||||
// so, we need to release the lock and re-advance
|
||||
std::lock_guard<std::mutex> lock(m_query_mutex);
|
||||
version = get_query_version();
|
||||
if (version.version == std::numeric_limits<uint_fast64_t>::max())
|
||||
return;
|
||||
if (version != sg.get_version_of_current_transaction())
|
||||
continue;
|
||||
|
||||
// Query version now matches the SG version, so we can deliver them
|
||||
for (auto& query : m_queries) {
|
||||
if (query->deliver(sg, m_async_error)) {
|
||||
queries.push_back(query);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto& query : queries) {
|
||||
query->call_callbacks();
|
||||
}
|
||||
}
|
||||
|
||||
void RealmCoordinator::process_available_async(Realm& realm)
|
||||
{
|
||||
auto& sg = Realm::Internal::get_shared_group(realm);
|
||||
decltype(m_queries) queries;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_query_mutex);
|
||||
for (auto& query : m_queries) {
|
||||
if (query->deliver(sg, m_async_error)) {
|
||||
queries.push_back(query);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& query : queries) {
|
||||
query->call_callbacks();
|
||||
}
|
||||
}
|
||||
467
Example/Pods/Realm/Realm/ObjectStore/impl/transact_log_handler.cpp
generated
Normal file
467
Example/Pods/Realm/Realm/ObjectStore/impl/transact_log_handler.cpp
generated
Normal file
@@ -0,0 +1,467 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2015 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.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "impl/transact_log_handler.hpp"
|
||||
|
||||
#include "binding_context.hpp"
|
||||
|
||||
#include <realm/commit_log.hpp>
|
||||
#include <realm/group_shared.hpp>
|
||||
#include <realm/lang_bind_helper.hpp>
|
||||
|
||||
using namespace realm;
|
||||
|
||||
namespace {
|
||||
// A transaction log handler that just validates that all operations made are
|
||||
// ones supported by the object store
|
||||
class TransactLogValidator {
|
||||
// Index of currently selected table
|
||||
size_t m_current_table = 0;
|
||||
|
||||
// Tables which were created during the transaction being processed, which
|
||||
// can have columns inserted without a schema version bump
|
||||
std::vector<size_t> m_new_tables;
|
||||
|
||||
REALM_NORETURN
|
||||
REALM_NOINLINE
|
||||
void schema_error()
|
||||
{
|
||||
throw std::runtime_error("Schema mismatch detected: another process has modified the Realm file's schema in an incompatible way");
|
||||
}
|
||||
|
||||
// Throw an exception if the currently modified table already existed before
|
||||
// the current set of modifications
|
||||
bool schema_error_unless_new_table()
|
||||
{
|
||||
if (std::find(begin(m_new_tables), end(m_new_tables), m_current_table) == end(m_new_tables)) {
|
||||
schema_error();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
size_t current_table() const noexcept { return m_current_table; }
|
||||
|
||||
public:
|
||||
// Schema changes which don't involve a change in the schema version are
|
||||
// allowed
|
||||
bool add_search_index(size_t) { return true; }
|
||||
bool remove_search_index(size_t) { return true; }
|
||||
|
||||
// Creating entirely new tables without a schema version bump is allowed, so
|
||||
// we need to track if new columns are being added to a new table or an
|
||||
// existing one
|
||||
bool insert_group_level_table(size_t table_ndx, size_t, StringData)
|
||||
{
|
||||
// Shift any previously added tables after the new one
|
||||
for (auto& table : m_new_tables) {
|
||||
if (table >= table_ndx)
|
||||
++table;
|
||||
}
|
||||
m_new_tables.push_back(table_ndx);
|
||||
return true;
|
||||
}
|
||||
bool insert_column(size_t, DataType, StringData, bool) { return schema_error_unless_new_table(); }
|
||||
bool insert_link_column(size_t, DataType, StringData, size_t, size_t) { return schema_error_unless_new_table(); }
|
||||
bool add_primary_key(size_t) { return schema_error_unless_new_table(); }
|
||||
bool set_link_type(size_t, LinkType) { return schema_error_unless_new_table(); }
|
||||
|
||||
// Removing or renaming things while a Realm is open is never supported
|
||||
bool erase_group_level_table(size_t, size_t) { schema_error(); }
|
||||
bool rename_group_level_table(size_t, StringData) { schema_error(); }
|
||||
bool erase_column(size_t) { schema_error(); }
|
||||
bool erase_link_column(size_t, size_t, size_t) { schema_error(); }
|
||||
bool rename_column(size_t, StringData) { schema_error(); }
|
||||
bool remove_primary_key() { schema_error(); }
|
||||
bool move_column(size_t, size_t) { schema_error(); }
|
||||
bool move_group_level_table(size_t, size_t) { schema_error(); }
|
||||
|
||||
bool select_descriptor(int levels, const size_t*)
|
||||
{
|
||||
// subtables not supported
|
||||
return levels == 0;
|
||||
}
|
||||
|
||||
bool select_table(size_t group_level_ndx, int, const size_t*) noexcept
|
||||
{
|
||||
m_current_table = group_level_ndx;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool select_link_list(size_t, size_t, size_t) { return true; }
|
||||
|
||||
// Non-schema changes are all allowed
|
||||
void parse_complete() { }
|
||||
bool insert_empty_rows(size_t, size_t, size_t, bool) { return true; }
|
||||
bool erase_rows(size_t, size_t, size_t, bool) { return true; }
|
||||
bool swap_rows(size_t, size_t) { return true; }
|
||||
bool clear_table() noexcept { return true; }
|
||||
bool link_list_set(size_t, size_t) { return true; }
|
||||
bool link_list_insert(size_t, size_t) { return true; }
|
||||
bool link_list_erase(size_t) { return true; }
|
||||
bool link_list_nullify(size_t) { return true; }
|
||||
bool link_list_clear(size_t) { return true; }
|
||||
bool link_list_move(size_t, size_t) { return true; }
|
||||
bool link_list_swap(size_t, size_t) { return true; }
|
||||
bool set_int(size_t, size_t, int_fast64_t) { return true; }
|
||||
bool set_bool(size_t, size_t, bool) { return true; }
|
||||
bool set_float(size_t, size_t, float) { return true; }
|
||||
bool set_double(size_t, size_t, double) { return true; }
|
||||
bool set_string(size_t, size_t, StringData) { return true; }
|
||||
bool set_binary(size_t, size_t, BinaryData) { return true; }
|
||||
bool set_date_time(size_t, size_t, DateTime) { return true; }
|
||||
bool set_table(size_t, size_t) { return true; }
|
||||
bool set_mixed(size_t, size_t, const Mixed&) { return true; }
|
||||
bool set_link(size_t, size_t, size_t, size_t) { return true; }
|
||||
bool set_null(size_t, size_t) { return true; }
|
||||
bool nullify_link(size_t, size_t, size_t) { return true; }
|
||||
bool insert_substring(size_t, size_t, size_t, StringData) { return true; }
|
||||
bool erase_substring(size_t, size_t, size_t, size_t) { return true; }
|
||||
bool optimize_table() { return true; }
|
||||
bool set_int_unique(size_t, size_t, size_t, int_fast64_t) { return true; }
|
||||
bool set_string_unique(size_t, size_t, size_t, StringData) { return true; }
|
||||
bool change_link_targets(size_t, size_t) { return true; }
|
||||
};
|
||||
|
||||
// Extends TransactLogValidator to also track changes and report it to the
|
||||
// binding context if any properties are being observed
|
||||
class TransactLogObserver : public TransactLogValidator {
|
||||
using ColumnInfo = BindingContext::ColumnInfo;
|
||||
using ObserverState = BindingContext::ObserverState;
|
||||
|
||||
// Observed table rows which need change information
|
||||
std::vector<ObserverState> m_observers;
|
||||
// Userdata pointers for rows which have been deleted
|
||||
std::vector<void *> invalidated;
|
||||
// Delegate to send change information to
|
||||
BindingContext* m_context;
|
||||
|
||||
// Change information for the currently selected LinkList, if any
|
||||
ColumnInfo* m_active_linklist = nullptr;
|
||||
|
||||
// Tables which were created during the transaction being processed, which
|
||||
// can have columns inserted without a schema version bump
|
||||
std::vector<size_t> m_new_tables;
|
||||
|
||||
// Get the change info for the given column, creating it if needed
|
||||
static ColumnInfo& get_change(ObserverState& state, size_t i)
|
||||
{
|
||||
if (state.changes.size() <= i) {
|
||||
state.changes.resize(std::max(state.changes.size() * 2, i + 1));
|
||||
}
|
||||
return state.changes[i];
|
||||
}
|
||||
|
||||
// Loop over the columns which were changed in an observer state
|
||||
template<typename Func>
|
||||
static void for_each(ObserverState& state, Func&& f)
|
||||
{
|
||||
for (size_t i = 0; i < state.changes.size(); ++i) {
|
||||
auto const& change = state.changes[i];
|
||||
if (change.changed) {
|
||||
f(i, change);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the given row/col as needing notifications sent
|
||||
bool mark_dirty(size_t row_ndx, size_t col_ndx)
|
||||
{
|
||||
auto it = lower_bound(begin(m_observers), end(m_observers), ObserverState{current_table(), row_ndx, nullptr});
|
||||
if (it != end(m_observers) && it->table_ndx == current_table() && it->row_ndx == row_ndx) {
|
||||
get_change(*it, col_ndx).changed = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Remove the given observer from the list of observed objects and add it
|
||||
// to the listed of invalidated objects
|
||||
void invalidate(ObserverState *o)
|
||||
{
|
||||
invalidated.push_back(o->info);
|
||||
m_observers.erase(m_observers.begin() + (o - &m_observers[0]));
|
||||
}
|
||||
|
||||
public:
|
||||
template<typename Func>
|
||||
TransactLogObserver(BindingContext* context, SharedGroup& sg, Func&& func, bool validate_schema_changes)
|
||||
: m_context(context)
|
||||
{
|
||||
if (!context) {
|
||||
if (validate_schema_changes) {
|
||||
// The handler functions are non-virtual, so the parent class's
|
||||
// versions are called if we don't need to track changes to observed
|
||||
// objects
|
||||
func(static_cast<TransactLogValidator&>(*this));
|
||||
}
|
||||
else {
|
||||
func();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
m_observers = context->get_observed_rows();
|
||||
if (m_observers.empty()) {
|
||||
auto old_version = sg.get_version_of_current_transaction();
|
||||
if (validate_schema_changes) {
|
||||
func(static_cast<TransactLogValidator&>(*this));
|
||||
}
|
||||
else {
|
||||
func();
|
||||
}
|
||||
if (old_version != sg.get_version_of_current_transaction()) {
|
||||
context->did_change({}, {});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
func(*this);
|
||||
context->did_change(m_observers, invalidated);
|
||||
}
|
||||
|
||||
// Called at the end of the transaction log immediately before the version
|
||||
// is advanced
|
||||
void parse_complete()
|
||||
{
|
||||
m_context->will_change(m_observers, invalidated);
|
||||
}
|
||||
|
||||
bool insert_group_level_table(size_t table_ndx, size_t prior_size, StringData name)
|
||||
{
|
||||
for (auto& observer : m_observers) {
|
||||
if (observer.table_ndx >= table_ndx)
|
||||
++observer.table_ndx;
|
||||
}
|
||||
TransactLogValidator::insert_group_level_table(table_ndx, prior_size, name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool insert_empty_rows(size_t, size_t, size_t, bool)
|
||||
{
|
||||
// rows are only inserted at the end, so no need to do anything
|
||||
return true;
|
||||
}
|
||||
|
||||
bool erase_rows(size_t row_ndx, size_t, size_t last_row_ndx, bool unordered)
|
||||
{
|
||||
for (size_t i = 0; i < m_observers.size(); ++i) {
|
||||
auto& o = m_observers[i];
|
||||
if (o.table_ndx == current_table()) {
|
||||
if (o.row_ndx == row_ndx) {
|
||||
invalidate(&o);
|
||||
--i;
|
||||
}
|
||||
else if (unordered && o.row_ndx == last_row_ndx) {
|
||||
o.row_ndx = row_ndx;
|
||||
}
|
||||
else if (!unordered && o.row_ndx > row_ndx) {
|
||||
o.row_ndx -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool clear_table()
|
||||
{
|
||||
for (size_t i = 0; i < m_observers.size(); ) {
|
||||
auto& o = m_observers[i];
|
||||
if (o.table_ndx == current_table()) {
|
||||
invalidate(&o);
|
||||
}
|
||||
else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool select_link_list(size_t col, size_t row, size_t)
|
||||
{
|
||||
m_active_linklist = nullptr;
|
||||
for (auto& o : m_observers) {
|
||||
if (o.table_ndx == current_table() && o.row_ndx == row) {
|
||||
m_active_linklist = &get_change(o, col);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void append_link_list_change(ColumnInfo::Kind kind, size_t index) {
|
||||
ColumnInfo *o = m_active_linklist;
|
||||
if (!o || o->kind == ColumnInfo::Kind::SetAll) {
|
||||
// Active LinkList isn't observed or already has multiple kinds of changes
|
||||
return;
|
||||
}
|
||||
|
||||
if (o->kind == ColumnInfo::Kind::None) {
|
||||
o->kind = kind;
|
||||
o->changed = true;
|
||||
o->indices.add(index);
|
||||
}
|
||||
else if (o->kind == kind) {
|
||||
if (kind == ColumnInfo::Kind::Remove) {
|
||||
o->indices.add_shifted(index);
|
||||
}
|
||||
else if (kind == ColumnInfo::Kind::Insert) {
|
||||
o->indices.insert_at(index);
|
||||
}
|
||||
else {
|
||||
o->indices.add(index);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Array KVO can only send a single kind of change at a time, so
|
||||
// if there's multiple just give up and send "Set"
|
||||
o->indices.set(0);
|
||||
o->kind = ColumnInfo::Kind::SetAll;
|
||||
}
|
||||
}
|
||||
|
||||
bool link_list_set(size_t index, size_t)
|
||||
{
|
||||
append_link_list_change(ColumnInfo::Kind::Set, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool link_list_insert(size_t index, size_t)
|
||||
{
|
||||
append_link_list_change(ColumnInfo::Kind::Insert, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool link_list_erase(size_t index)
|
||||
{
|
||||
append_link_list_change(ColumnInfo::Kind::Remove, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool link_list_nullify(size_t index)
|
||||
{
|
||||
append_link_list_change(ColumnInfo::Kind::Remove, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool link_list_swap(size_t index1, size_t index2)
|
||||
{
|
||||
append_link_list_change(ColumnInfo::Kind::Set, index1);
|
||||
append_link_list_change(ColumnInfo::Kind::Set, index2);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool link_list_clear(size_t old_size)
|
||||
{
|
||||
ColumnInfo *o = m_active_linklist;
|
||||
if (!o || o->kind == ColumnInfo::Kind::SetAll) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (o->kind == ColumnInfo::Kind::Remove)
|
||||
old_size += o->indices.size();
|
||||
else if (o->kind == ColumnInfo::Kind::Insert)
|
||||
old_size -= o->indices.size();
|
||||
|
||||
o->indices.set(old_size);
|
||||
|
||||
o->kind = ColumnInfo::Kind::Remove;
|
||||
o->changed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool link_list_move(size_t from, size_t to)
|
||||
{
|
||||
ColumnInfo *o = m_active_linklist;
|
||||
if (!o || o->kind == ColumnInfo::Kind::SetAll) {
|
||||
return true;
|
||||
}
|
||||
if (from > to) {
|
||||
std::swap(from, to);
|
||||
}
|
||||
|
||||
if (o->kind == ColumnInfo::Kind::None) {
|
||||
o->kind = ColumnInfo::Kind::Set;
|
||||
o->changed = true;
|
||||
}
|
||||
if (o->kind == ColumnInfo::Kind::Set) {
|
||||
for (size_t i = from; i <= to; ++i)
|
||||
o->indices.add(i);
|
||||
}
|
||||
else {
|
||||
o->indices.set(0);
|
||||
o->kind = ColumnInfo::Kind::SetAll;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Things that just mark the field as modified
|
||||
bool set_int(size_t col, size_t row, int_fast64_t) { return mark_dirty(row, col); }
|
||||
bool set_bool(size_t col, size_t row, bool) { return mark_dirty(row, col); }
|
||||
bool set_float(size_t col, size_t row, float) { return mark_dirty(row, col); }
|
||||
bool set_double(size_t col, size_t row, double) { return mark_dirty(row, col); }
|
||||
bool set_string(size_t col, size_t row, StringData) { return mark_dirty(row, col); }
|
||||
bool set_binary(size_t col, size_t row, BinaryData) { return mark_dirty(row, col); }
|
||||
bool set_date_time(size_t col, size_t row, DateTime) { return mark_dirty(row, col); }
|
||||
bool set_table(size_t col, size_t row) { return mark_dirty(row, col); }
|
||||
bool set_mixed(size_t col, size_t row, const Mixed&) { return mark_dirty(row, col); }
|
||||
bool set_link(size_t col, size_t row, size_t, size_t) { return mark_dirty(row, col); }
|
||||
bool set_null(size_t col, size_t row) { return mark_dirty(row, col); }
|
||||
bool nullify_link(size_t col, size_t row, size_t) { return mark_dirty(row, col); }
|
||||
bool set_int_unique(size_t col, size_t row, size_t, int_fast64_t) { return mark_dirty(row, col); }
|
||||
bool set_string_unique(size_t col, size_t row, size_t, StringData) { return mark_dirty(row, col); }
|
||||
bool insert_substring(size_t col, size_t row, size_t, StringData) { return mark_dirty(row, col); }
|
||||
bool erase_substring(size_t col, size_t row, size_t, size_t) { return mark_dirty(row, col); }
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
namespace realm {
|
||||
namespace _impl {
|
||||
namespace transaction {
|
||||
void advance(SharedGroup& sg, BindingContext* context, SharedGroup::VersionID version)
|
||||
{
|
||||
TransactLogObserver(context, sg, [&](auto&&... args) {
|
||||
LangBindHelper::advance_read(sg, std::move(args)..., version);
|
||||
}, true);
|
||||
}
|
||||
|
||||
void begin(SharedGroup& sg, BindingContext* context, bool validate_schema_changes)
|
||||
{
|
||||
TransactLogObserver(context, sg, [&](auto&&... args) {
|
||||
LangBindHelper::promote_to_write(sg, std::move(args)...);
|
||||
}, validate_schema_changes);
|
||||
}
|
||||
|
||||
void commit(SharedGroup& sg, BindingContext* context)
|
||||
{
|
||||
LangBindHelper::commit_and_continue_as_read(sg);
|
||||
|
||||
if (context) {
|
||||
context->did_change({}, {});
|
||||
}
|
||||
}
|
||||
|
||||
void cancel(SharedGroup& sg, BindingContext* context)
|
||||
{
|
||||
TransactLogObserver(context, sg, [&](auto&&... args) {
|
||||
LangBindHelper::rollback_and_continue_as_read(sg, std::move(args)...);
|
||||
}, false);
|
||||
}
|
||||
|
||||
} // namespace transaction
|
||||
} // namespace _impl
|
||||
} // namespace realm
|
||||
92
Example/Pods/Realm/Realm/ObjectStore/index_set.cpp
generated
Normal file
92
Example/Pods/Realm/Realm/ObjectStore/index_set.cpp
generated
Normal file
@@ -0,0 +1,92 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2015 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.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "index_set.hpp"
|
||||
|
||||
using namespace realm;
|
||||
|
||||
IndexSet::iterator IndexSet::find(size_t index)
|
||||
{
|
||||
for (auto it = m_ranges.begin(), end = m_ranges.end(); it != end; ++it) {
|
||||
if (it->second > index)
|
||||
return it;
|
||||
}
|
||||
return m_ranges.end();
|
||||
}
|
||||
|
||||
void IndexSet::add(size_t index)
|
||||
{
|
||||
do_add(find(index), index);
|
||||
}
|
||||
|
||||
void IndexSet::do_add(iterator it, size_t index)
|
||||
{
|
||||
bool more_before = it != m_ranges.begin(), valid = it != m_ranges.end();
|
||||
if (valid && it->first <= index && it->second > index) {
|
||||
// index is already in set
|
||||
}
|
||||
else if (more_before && (it - 1)->second == index) {
|
||||
// index is immediately after an existing range
|
||||
++(it - 1)->second;
|
||||
}
|
||||
else if (more_before && valid && (it - 1)->second == it->first) {
|
||||
// index joins two existing ranges
|
||||
(it - 1)->second = it->second;
|
||||
m_ranges.erase(it);
|
||||
}
|
||||
else if (valid && it->first == index + 1) {
|
||||
// index is immediately before an existing range
|
||||
--it->first;
|
||||
}
|
||||
else {
|
||||
// index is not next to an existing range
|
||||
m_ranges.insert(it, {index, index + 1});
|
||||
}
|
||||
}
|
||||
|
||||
void IndexSet::set(size_t len)
|
||||
{
|
||||
m_ranges.clear();
|
||||
if (len) {
|
||||
m_ranges.push_back({0, len});
|
||||
}
|
||||
}
|
||||
|
||||
void IndexSet::insert_at(size_t index)
|
||||
{
|
||||
auto pos = find(index);
|
||||
if (pos != m_ranges.end()) {
|
||||
if (pos->first >= index)
|
||||
++pos->first;
|
||||
++pos->second;
|
||||
for (auto it = pos + 1; it != m_ranges.end(); ++it) {
|
||||
++it->first;
|
||||
++it->second;
|
||||
}
|
||||
}
|
||||
do_add(pos, index);
|
||||
}
|
||||
|
||||
void IndexSet::add_shifted(size_t index)
|
||||
{
|
||||
auto it = m_ranges.begin();
|
||||
for (auto end = m_ranges.end(); it != end && it->first <= index; ++it) {
|
||||
index += it->second - it->first;
|
||||
}
|
||||
do_add(it, index);
|
||||
}
|
||||
86
Example/Pods/Realm/Realm/ObjectStore/object_schema.cpp
generated
Normal file
86
Example/Pods/Realm/Realm/ObjectStore/object_schema.cpp
generated
Normal file
@@ -0,0 +1,86 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2015 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.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "object_schema.hpp"
|
||||
|
||||
#include "object_store.hpp"
|
||||
#include "property.hpp"
|
||||
|
||||
#include <realm/group_shared.hpp>
|
||||
#include <realm/link_view.hpp>
|
||||
|
||||
using namespace realm;
|
||||
|
||||
ObjectSchema::~ObjectSchema() = default;
|
||||
|
||||
ObjectSchema::ObjectSchema(std::string name, std::string primary_key, std::initializer_list<Property> properties)
|
||||
: name(std::move(name))
|
||||
, properties(properties)
|
||||
, primary_key(std::move(primary_key))
|
||||
{
|
||||
set_primary_key_property();
|
||||
}
|
||||
|
||||
ObjectSchema::ObjectSchema(const Group *group, const std::string &name) : name(name) {
|
||||
ConstTableRef table = ObjectStore::table_for_object_type(group, name);
|
||||
|
||||
size_t count = table->get_column_count();
|
||||
properties.reserve(count);
|
||||
for (size_t col = 0; col < count; col++) {
|
||||
Property property;
|
||||
property.name = table->get_column_name(col).data();
|
||||
property.type = (PropertyType)table->get_column_type(col);
|
||||
property.is_indexed = table->has_search_index(col);
|
||||
property.is_primary = false;
|
||||
property.is_nullable = table->is_nullable(col) || property.type == PropertyTypeObject;
|
||||
property.table_column = col;
|
||||
if (property.type == PropertyTypeObject || property.type == PropertyTypeArray) {
|
||||
// set link type for objects and arrays
|
||||
ConstTableRef linkTable = table->get_link_target(col);
|
||||
property.object_type = ObjectStore::object_type_for_table_name(linkTable->get_name().data());
|
||||
}
|
||||
properties.push_back(std::move(property));
|
||||
}
|
||||
|
||||
primary_key = realm::ObjectStore::get_primary_key_for_object(group, name);
|
||||
set_primary_key_property();
|
||||
}
|
||||
|
||||
Property *ObjectSchema::property_for_name(StringData name) {
|
||||
for (auto& prop : properties) {
|
||||
if (StringData(prop.name) == name) {
|
||||
return ∝
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Property *ObjectSchema::property_for_name(StringData name) const {
|
||||
return const_cast<ObjectSchema *>(this)->property_for_name(name);
|
||||
}
|
||||
|
||||
void ObjectSchema::set_primary_key_property()
|
||||
{
|
||||
if (primary_key.length()) {
|
||||
auto primary_key_prop = primary_key_property();
|
||||
if (!primary_key_prop) {
|
||||
throw InvalidPrimaryKeyException(name, primary_key);
|
||||
}
|
||||
primary_key_prop->is_primary = true;
|
||||
}
|
||||
}
|
||||
603
Example/Pods/Realm/Realm/ObjectStore/object_store.cpp
generated
Normal file
603
Example/Pods/Realm/Realm/ObjectStore/object_store.cpp
generated
Normal file
@@ -0,0 +1,603 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2015 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.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "object_store.hpp"
|
||||
|
||||
#include "schema.hpp"
|
||||
|
||||
#include <realm/group.hpp>
|
||||
#include <realm/link_view.hpp>
|
||||
#include <realm/table.hpp>
|
||||
#include <realm/table_view.hpp>
|
||||
#include <realm/util/assert.hpp>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
using namespace realm;
|
||||
|
||||
namespace {
|
||||
const char * const c_metadataTableName = "metadata";
|
||||
const char * const c_versionColumnName = "version";
|
||||
const size_t c_versionColumnIndex = 0;
|
||||
|
||||
const char * const c_primaryKeyTableName = "pk";
|
||||
const char * const c_primaryKeyObjectClassColumnName = "pk_table";
|
||||
const size_t c_primaryKeyObjectClassColumnIndex = 0;
|
||||
const char * const c_primaryKeyPropertyNameColumnName = "pk_property";
|
||||
const size_t c_primaryKeyPropertyNameColumnIndex = 1;
|
||||
|
||||
const size_t c_zeroRowIndex = 0;
|
||||
|
||||
const char c_object_table_prefix[] = "class_";
|
||||
}
|
||||
|
||||
const uint64_t ObjectStore::NotVersioned = std::numeric_limits<uint64_t>::max();
|
||||
|
||||
bool ObjectStore::has_metadata_tables(const Group *group) {
|
||||
return group->get_table(c_primaryKeyTableName) && group->get_table(c_metadataTableName);
|
||||
}
|
||||
|
||||
void ObjectStore::create_metadata_tables(Group *group) {
|
||||
TableRef table = group->get_or_add_table(c_primaryKeyTableName);
|
||||
if (table->get_column_count() == 0) {
|
||||
table->add_column(type_String, c_primaryKeyObjectClassColumnName);
|
||||
table->add_column(type_String, c_primaryKeyPropertyNameColumnName);
|
||||
}
|
||||
|
||||
table = group->get_or_add_table(c_metadataTableName);
|
||||
if (table->get_column_count() == 0) {
|
||||
table->add_column(type_Int, c_versionColumnName);
|
||||
|
||||
// set initial version
|
||||
table->add_empty_row();
|
||||
table->set_int(c_versionColumnIndex, c_zeroRowIndex, ObjectStore::NotVersioned);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t ObjectStore::get_schema_version(const Group *group) {
|
||||
ConstTableRef table = group->get_table(c_metadataTableName);
|
||||
if (!table || table->get_column_count() == 0) {
|
||||
return ObjectStore::NotVersioned;
|
||||
}
|
||||
return table->get_int(c_versionColumnIndex, c_zeroRowIndex);
|
||||
}
|
||||
|
||||
void ObjectStore::set_schema_version(Group *group, uint64_t version) {
|
||||
TableRef table = group->get_or_add_table(c_metadataTableName);
|
||||
table->set_int(c_versionColumnIndex, c_zeroRowIndex, version);
|
||||
}
|
||||
|
||||
StringData ObjectStore::get_primary_key_for_object(const Group *group, StringData object_type) {
|
||||
ConstTableRef table = group->get_table(c_primaryKeyTableName);
|
||||
if (!table) {
|
||||
return "";
|
||||
}
|
||||
size_t row = table->find_first_string(c_primaryKeyObjectClassColumnIndex, object_type);
|
||||
if (row == not_found) {
|
||||
return "";
|
||||
}
|
||||
return table->get_string(c_primaryKeyPropertyNameColumnIndex, row);
|
||||
}
|
||||
|
||||
void ObjectStore::set_primary_key_for_object(Group *group, StringData object_type, StringData primary_key) {
|
||||
TableRef table = group->get_table(c_primaryKeyTableName);
|
||||
|
||||
// get row or create if new object and populate
|
||||
size_t row = table->find_first_string(c_primaryKeyObjectClassColumnIndex, object_type);
|
||||
if (row == not_found && primary_key.size()) {
|
||||
row = table->add_empty_row();
|
||||
table->set_string(c_primaryKeyObjectClassColumnIndex, row, object_type);
|
||||
}
|
||||
|
||||
// set if changing, or remove if setting to nil
|
||||
if (primary_key.size() == 0) {
|
||||
if (row != not_found) {
|
||||
table->remove(row);
|
||||
}
|
||||
}
|
||||
else {
|
||||
table->set_string(c_primaryKeyPropertyNameColumnIndex, row, primary_key);
|
||||
}
|
||||
}
|
||||
|
||||
StringData ObjectStore::object_type_for_table_name(StringData table_name) {
|
||||
if (table_name.begins_with(c_object_table_prefix)) {
|
||||
return table_name.substr(sizeof(c_object_table_prefix) - 1);
|
||||
}
|
||||
return StringData();
|
||||
}
|
||||
|
||||
std::string ObjectStore::table_name_for_object_type(StringData object_type) {
|
||||
return std::string(c_object_table_prefix) + object_type.data();
|
||||
}
|
||||
|
||||
TableRef ObjectStore::table_for_object_type(Group *group, StringData object_type) {
|
||||
return group->get_table(table_name_for_object_type(object_type));
|
||||
}
|
||||
|
||||
ConstTableRef ObjectStore::table_for_object_type(const Group *group, StringData object_type) {
|
||||
return group->get_table(table_name_for_object_type(object_type));
|
||||
}
|
||||
|
||||
TableRef ObjectStore::table_for_object_type_create_if_needed(Group *group, StringData object_type, bool &created) {
|
||||
return group->get_or_add_table(table_name_for_object_type(object_type), &created);
|
||||
}
|
||||
|
||||
static inline bool property_has_changed(Property const& p1, Property const& p2) {
|
||||
return p1.type != p2.type
|
||||
|| p1.name != p2.name
|
||||
|| p1.object_type != p2.object_type
|
||||
|| p1.is_nullable != p2.is_nullable;
|
||||
}
|
||||
|
||||
static inline bool property_can_be_migrated_to_nullable(const Property& old_property, const Property& new_property) {
|
||||
return old_property.type == new_property.type
|
||||
&& !old_property.is_nullable
|
||||
&& new_property.is_nullable
|
||||
&& new_property.name == old_property.name;
|
||||
}
|
||||
|
||||
void ObjectStore::verify_schema(Schema const& actual_schema, Schema& target_schema, bool allow_missing_tables) {
|
||||
std::vector<ObjectSchemaValidationException> errors;
|
||||
for (auto &object_schema : target_schema) {
|
||||
auto matching_schema = actual_schema.find(object_schema);
|
||||
if (matching_schema == actual_schema.end()) {
|
||||
if (!allow_missing_tables) {
|
||||
errors.emplace_back(ObjectSchemaValidationException(object_schema.name,
|
||||
"Missing table for object type '" + object_schema.name + "'."));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
auto more_errors = verify_object_schema(*matching_schema, object_schema);
|
||||
errors.insert(errors.end(), more_errors.begin(), more_errors.end());
|
||||
}
|
||||
if (errors.size()) {
|
||||
throw SchemaMismatchException(errors);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ObjectSchemaValidationException> ObjectStore::verify_object_schema(ObjectSchema const& table_schema,
|
||||
ObjectSchema& target_schema) {
|
||||
std::vector<ObjectSchemaValidationException> exceptions;
|
||||
|
||||
// check to see if properties are the same
|
||||
for (auto& current_prop : table_schema.properties) {
|
||||
auto target_prop = target_schema.property_for_name(current_prop.name);
|
||||
|
||||
if (!target_prop) {
|
||||
exceptions.emplace_back(MissingPropertyException(table_schema.name, current_prop));
|
||||
continue;
|
||||
}
|
||||
if (property_has_changed(current_prop, *target_prop)) {
|
||||
exceptions.emplace_back(MismatchedPropertiesException(table_schema.name, current_prop, *target_prop));
|
||||
continue;
|
||||
}
|
||||
|
||||
// create new property with aligned column
|
||||
target_prop->table_column = current_prop.table_column;
|
||||
}
|
||||
|
||||
// check for change to primary key
|
||||
if (table_schema.primary_key != target_schema.primary_key) {
|
||||
exceptions.emplace_back(ChangedPrimaryKeyException(table_schema.name, table_schema.primary_key, target_schema.primary_key));
|
||||
}
|
||||
|
||||
// check for new missing properties
|
||||
for (auto& target_prop : target_schema.properties) {
|
||||
if (!table_schema.property_for_name(target_prop.name)) {
|
||||
exceptions.emplace_back(ExtraPropertyException(table_schema.name, target_prop));
|
||||
}
|
||||
}
|
||||
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void copy_property_values(const Property& old_property, const Property& new_property, Table& table,
|
||||
T (Table::*getter)(std::size_t, std::size_t) const noexcept,
|
||||
void (Table::*setter)(std::size_t, std::size_t, T)) {
|
||||
size_t old_column = old_property.table_column, new_column = new_property.table_column;
|
||||
size_t count = table.size();
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
(table.*setter)(new_column, i, (table.*getter)(old_column, i));
|
||||
}
|
||||
}
|
||||
|
||||
static void copy_property_values(const Property& source, const Property& destination, Table& table) {
|
||||
switch (destination.type) {
|
||||
case PropertyTypeInt:
|
||||
copy_property_values(source, destination, table, &Table::get_int, &Table::set_int);
|
||||
break;
|
||||
case PropertyTypeBool:
|
||||
copy_property_values(source, destination, table, &Table::get_bool, &Table::set_bool);
|
||||
break;
|
||||
case PropertyTypeFloat:
|
||||
copy_property_values(source, destination, table, &Table::get_float, &Table::set_float);
|
||||
break;
|
||||
case PropertyTypeDouble:
|
||||
copy_property_values(source, destination, table, &Table::get_double, &Table::set_double);
|
||||
break;
|
||||
case PropertyTypeString:
|
||||
copy_property_values(source, destination, table, &Table::get_string, &Table::set_string);
|
||||
break;
|
||||
case PropertyTypeData:
|
||||
copy_property_values(source, destination, table, &Table::get_binary, &Table::set_binary);
|
||||
break;
|
||||
case PropertyTypeDate:
|
||||
copy_property_values(source, destination, table, &Table::get_datetime, &Table::set_datetime);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// set references to tables on targetSchema and create/update any missing or out-of-date tables
|
||||
// if update existing is true, updates existing tables, otherwise validates existing tables
|
||||
// NOTE: must be called from within write transaction
|
||||
void ObjectStore::create_tables(Group *group, Schema &target_schema, bool update_existing) {
|
||||
// first pass to create missing tables
|
||||
std::vector<ObjectSchema *> to_update;
|
||||
for (auto& object_schema : target_schema) {
|
||||
bool created = false;
|
||||
ObjectStore::table_for_object_type_create_if_needed(group, object_schema.name, created);
|
||||
|
||||
// we will modify tables for any new objectSchema (table was created) or for all if update_existing is true
|
||||
if (update_existing || created) {
|
||||
to_update.push_back(&object_schema);
|
||||
}
|
||||
}
|
||||
|
||||
// second pass adds/removes columns for out of date tables
|
||||
for (auto& target_object_schema : to_update) {
|
||||
TableRef table = table_for_object_type(group, target_object_schema->name);
|
||||
ObjectSchema current_schema(group, target_object_schema->name);
|
||||
std::vector<Property> &target_props = target_object_schema->properties;
|
||||
|
||||
// handle columns changing from required to optional
|
||||
for (auto& current_prop : current_schema.properties) {
|
||||
auto target_prop = target_object_schema->property_for_name(current_prop.name);
|
||||
if (!target_prop || !property_can_be_migrated_to_nullable(current_prop, *target_prop))
|
||||
continue;
|
||||
|
||||
target_prop->table_column = current_prop.table_column;
|
||||
current_prop.table_column = current_prop.table_column + 1;
|
||||
|
||||
table->insert_column(target_prop->table_column, DataType(target_prop->type), target_prop->name, target_prop->is_nullable);
|
||||
copy_property_values(current_prop, *target_prop, *table);
|
||||
table->remove_column(current_prop.table_column);
|
||||
|
||||
current_prop.table_column = target_prop->table_column;
|
||||
}
|
||||
|
||||
bool inserted_placeholder_column = false;
|
||||
|
||||
// remove extra columns
|
||||
size_t deleted = 0;
|
||||
for (auto& current_prop : current_schema.properties) {
|
||||
current_prop.table_column -= deleted;
|
||||
|
||||
auto target_prop = target_object_schema->property_for_name(current_prop.name);
|
||||
if (!target_prop || (property_has_changed(current_prop, *target_prop)
|
||||
&& !property_can_be_migrated_to_nullable(current_prop, *target_prop))) {
|
||||
if (deleted == current_schema.properties.size() - 1) {
|
||||
// We're about to remove the last column from the table. Insert a placeholder column to preserve
|
||||
// the number of rows in the table for the addition of new columns below.
|
||||
table->add_column(type_Bool, "placeholder");
|
||||
inserted_placeholder_column = true;
|
||||
}
|
||||
|
||||
table->remove_column(current_prop.table_column);
|
||||
++deleted;
|
||||
current_prop.table_column = npos;
|
||||
}
|
||||
}
|
||||
|
||||
// add missing columns
|
||||
for (auto& target_prop : target_props) {
|
||||
auto current_prop = current_schema.property_for_name(target_prop.name);
|
||||
|
||||
// add any new properties (no old column or old column was removed due to not matching)
|
||||
if (!current_prop || current_prop->table_column == npos) {
|
||||
switch (target_prop.type) {
|
||||
// for objects and arrays, we have to specify target table
|
||||
case PropertyTypeObject:
|
||||
case PropertyTypeArray: {
|
||||
TableRef link_table = ObjectStore::table_for_object_type(group, target_prop.object_type);
|
||||
REALM_ASSERT(link_table);
|
||||
target_prop.table_column = table->add_column_link(DataType(target_prop.type), target_prop.name, *link_table);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
target_prop.table_column = table->add_column(DataType(target_prop.type),
|
||||
target_prop.name,
|
||||
target_prop.is_nullable);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
target_prop.table_column = current_prop->table_column;
|
||||
}
|
||||
}
|
||||
|
||||
if (inserted_placeholder_column) {
|
||||
// We inserted a placeholder due to removing all columns from the table. Remove it, and update the indices
|
||||
// of any columns that we inserted after it.
|
||||
table->remove_column(0);
|
||||
for (auto& target_prop : target_props) {
|
||||
target_prop.table_column--;
|
||||
}
|
||||
}
|
||||
|
||||
// update table metadata
|
||||
if (target_object_schema->primary_key.length()) {
|
||||
// if there is a primary key set, check if it is the same as the old key
|
||||
if (current_schema.primary_key != target_object_schema->primary_key) {
|
||||
set_primary_key_for_object(group, target_object_schema->name, target_object_schema->primary_key);
|
||||
}
|
||||
}
|
||||
else if (current_schema.primary_key.length()) {
|
||||
// there is no primary key, so if there was one nil out
|
||||
set_primary_key_for_object(group, target_object_schema->name, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ObjectStore::is_schema_at_version(const Group *group, uint64_t version) {
|
||||
uint64_t old_version = get_schema_version(group);
|
||||
if (old_version > version && old_version != NotVersioned) {
|
||||
throw InvalidSchemaVersionException(old_version, version);
|
||||
}
|
||||
return old_version == version;
|
||||
}
|
||||
|
||||
bool ObjectStore::needs_update(Schema const& old_schema, Schema const& schema) {
|
||||
for (auto const& target_schema : schema) {
|
||||
auto matching_schema = old_schema.find(target_schema);
|
||||
if (matching_schema == end(old_schema)) {
|
||||
// Table doesn't exist
|
||||
return true;
|
||||
}
|
||||
|
||||
if (matching_schema->properties.size() != target_schema.properties.size()) {
|
||||
// If the number of properties don't match then a migration is required
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that all of the property indexes are up to date
|
||||
for (size_t i = 0, count = target_schema.properties.size(); i < count; ++i) {
|
||||
if (target_schema.properties[i].is_indexed != matching_schema->properties[i].is_indexed) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ObjectStore::update_realm_with_schema(Group *group, Schema const& old_schema,
|
||||
uint64_t version, Schema &schema,
|
||||
MigrationFunction migration) {
|
||||
// Recheck the schema version after beginning the write transaction as
|
||||
// another process may have done the migration after we opened the read
|
||||
// transaction
|
||||
bool migrating = !is_schema_at_version(group, version);
|
||||
|
||||
// create tables
|
||||
create_metadata_tables(group);
|
||||
create_tables(group, schema, migrating);
|
||||
|
||||
if (!migrating) {
|
||||
// If we aren't migrating, then verify that all of the tables which
|
||||
// were already present are valid (newly created ones always are)
|
||||
verify_schema(old_schema, schema, true);
|
||||
}
|
||||
|
||||
update_indexes(group, schema);
|
||||
|
||||
if (!migrating) {
|
||||
return;
|
||||
}
|
||||
|
||||
// apply the migration block if provided and there's any old data
|
||||
if (get_schema_version(group) != ObjectStore::NotVersioned) {
|
||||
migration(group, schema);
|
||||
|
||||
validate_primary_column_uniqueness(group, schema);
|
||||
}
|
||||
|
||||
set_schema_version(group, version);
|
||||
}
|
||||
|
||||
Schema ObjectStore::schema_from_group(const Group *group) {
|
||||
std::vector<ObjectSchema> schema;
|
||||
for (size_t i = 0; i < group->size(); i++) {
|
||||
std::string object_type = object_type_for_table_name(group->get_table_name(i));
|
||||
if (object_type.length()) {
|
||||
schema.emplace_back(group, object_type);
|
||||
}
|
||||
}
|
||||
return schema;
|
||||
}
|
||||
|
||||
bool ObjectStore::update_indexes(Group *group, Schema &schema) {
|
||||
bool changed = false;
|
||||
for (auto& object_schema : schema) {
|
||||
TableRef table = table_for_object_type(group, object_schema.name);
|
||||
if (!table) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto& property : object_schema.properties) {
|
||||
if (property.requires_index() == table->has_search_index(property.table_column)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
if (property.requires_index()) {
|
||||
try {
|
||||
table->add_search_index(property.table_column);
|
||||
}
|
||||
catch (LogicError const&) {
|
||||
throw PropertyTypeNotIndexableException(object_schema.name, property);
|
||||
}
|
||||
}
|
||||
else {
|
||||
table->remove_search_index(property.table_column);
|
||||
}
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
void ObjectStore::validate_primary_column_uniqueness(const Group *group, Schema const& schema) {
|
||||
for (auto& object_schema : schema) {
|
||||
auto primary_prop = object_schema.primary_key_property();
|
||||
if (!primary_prop) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ConstTableRef table = table_for_object_type(group, object_schema.name);
|
||||
if (table->get_distinct_view(primary_prop->table_column).size() != table->size()) {
|
||||
throw DuplicatePrimaryKeyValueException(object_schema.name, *primary_prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectStore::delete_data_for_object(Group *group, StringData object_type) {
|
||||
TableRef table = table_for_object_type(group, object_type);
|
||||
if (table) {
|
||||
group->remove_table(table->get_index_in_group());
|
||||
set_primary_key_for_object(group, object_type, "");
|
||||
}
|
||||
}
|
||||
|
||||
bool ObjectStore::is_empty(const Group *group) {
|
||||
for (size_t i = 0; i < group->size(); i++) {
|
||||
ConstTableRef table = group->get_table(i);
|
||||
std::string object_type = object_type_for_table_name(table->get_name());
|
||||
if (!object_type.length()) {
|
||||
continue;
|
||||
}
|
||||
if (!table->is_empty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
InvalidSchemaVersionException::InvalidSchemaVersionException(uint64_t old_version, uint64_t new_version) :
|
||||
m_old_version(old_version), m_new_version(new_version)
|
||||
{
|
||||
m_what = "Provided schema version " + std::to_string(new_version) + " is less than last set version " + std::to_string(old_version) + ".";
|
||||
}
|
||||
|
||||
DuplicatePrimaryKeyValueException::DuplicatePrimaryKeyValueException(std::string const& object_type, Property const& property) :
|
||||
m_object_type(object_type), m_property(property)
|
||||
{
|
||||
m_what = "Primary key property '" + property.name + "' has duplicate values after migration.";
|
||||
}
|
||||
|
||||
SchemaValidationException::SchemaValidationException(std::vector<ObjectSchemaValidationException> const& errors) :
|
||||
m_validation_errors(errors)
|
||||
{
|
||||
m_what = "Schema validation failed due to the following errors: ";
|
||||
for (auto const& error : errors) {
|
||||
m_what += std::string("\n- ") + error.what();
|
||||
}
|
||||
}
|
||||
|
||||
SchemaMismatchException::SchemaMismatchException(std::vector<ObjectSchemaValidationException> const& errors) :
|
||||
m_validation_errors(errors)
|
||||
{
|
||||
m_what ="Migration is required due to the following errors: ";
|
||||
for (auto const& error : errors) {
|
||||
m_what += std::string("\n- ") + error.what();
|
||||
}
|
||||
}
|
||||
|
||||
PropertyTypeNotIndexableException::PropertyTypeNotIndexableException(std::string const& object_type, Property const& property) :
|
||||
ObjectSchemaPropertyException(object_type, property)
|
||||
{
|
||||
m_what = "Can't index property " + object_type + "." + property.name + ": indexing a property of type '" + string_for_property_type(property.type) + "' is currently not supported";
|
||||
}
|
||||
|
||||
ExtraPropertyException::ExtraPropertyException(std::string const& object_type, Property const& property) :
|
||||
ObjectSchemaPropertyException(object_type, property)
|
||||
{
|
||||
m_what = "Property '" + property.name + "' has been added to latest object model.";
|
||||
}
|
||||
|
||||
MissingPropertyException::MissingPropertyException(std::string const& object_type, Property const& property) :
|
||||
ObjectSchemaPropertyException(object_type, property)
|
||||
{
|
||||
m_what = "Property '" + property.name + "' is missing from latest object model.";
|
||||
}
|
||||
|
||||
InvalidNullabilityException::InvalidNullabilityException(std::string const& object_type, Property const& property) :
|
||||
ObjectSchemaPropertyException(object_type, property)
|
||||
{
|
||||
if (property.type == PropertyTypeObject) {
|
||||
m_what = "'Object' property '" + property.name + "' must be nullable.";
|
||||
}
|
||||
else {
|
||||
m_what = "Array or Mixed property '" + property.name + "' cannot be nullable";
|
||||
}
|
||||
}
|
||||
|
||||
MissingObjectTypeException::MissingObjectTypeException(std::string const& object_type, Property const& property) :
|
||||
ObjectSchemaPropertyException(object_type, property)
|
||||
{
|
||||
m_what = "Target type '" + property.object_type + "' doesn't exist for property '" + property.name + "'.";
|
||||
}
|
||||
|
||||
MismatchedPropertiesException::MismatchedPropertiesException(std::string const& object_type, Property const& old_property, Property const& new_property) :
|
||||
ObjectSchemaValidationException(object_type), m_old_property(old_property), m_new_property(new_property)
|
||||
{
|
||||
if (new_property.type != old_property.type) {
|
||||
m_what = "Property types for '" + old_property.name + "' property do not match. Old type '" + string_for_property_type(old_property.type) +
|
||||
"', new type '" + string_for_property_type(new_property.type) + "'";
|
||||
}
|
||||
else if (new_property.object_type != old_property.object_type) {
|
||||
m_what = "Target object type for property '" + old_property.name + "' do not match. Old type '" + old_property.object_type + "', new type '" + new_property.object_type + "'";
|
||||
}
|
||||
else if (new_property.is_nullable != old_property.is_nullable) {
|
||||
m_what = "Nullability for property '" + old_property.name + "' has changed from '" + std::to_string(old_property.is_nullable) + "' to '" + std::to_string(new_property.is_nullable) + "'.";
|
||||
}
|
||||
}
|
||||
|
||||
ChangedPrimaryKeyException::ChangedPrimaryKeyException(std::string const& object_type, std::string const& old_primary, std::string const& new_primary) : ObjectSchemaValidationException(object_type), m_old_primary(old_primary), m_new_primary(new_primary)
|
||||
{
|
||||
if (old_primary.size()) {
|
||||
m_what = "Property '" + old_primary + "' is no longer a primary key.";
|
||||
}
|
||||
else {
|
||||
m_what = "Property '" + new_primary + "' has been made a primary key.";
|
||||
}
|
||||
}
|
||||
|
||||
InvalidPrimaryKeyException::InvalidPrimaryKeyException(std::string const& object_type, std::string const& primary) :
|
||||
ObjectSchemaValidationException(object_type), m_primary_key(primary)
|
||||
{
|
||||
m_what = "Specified primary key property '" + primary + "' does not exist.";
|
||||
}
|
||||
|
||||
DuplicatePrimaryKeysException::DuplicatePrimaryKeysException(std::string const& object_type) : ObjectSchemaValidationException(object_type)
|
||||
{
|
||||
m_what = "Duplicate primary keys for object '" + object_type + "'.";
|
||||
}
|
||||
424
Example/Pods/Realm/Realm/ObjectStore/results.cpp
generated
Normal file
424
Example/Pods/Realm/Realm/ObjectStore/results.cpp
generated
Normal file
@@ -0,0 +1,424 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2015 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.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "results.hpp"
|
||||
|
||||
#include "impl/async_query.hpp"
|
||||
#include "impl/realm_coordinator.hpp"
|
||||
#include "object_store.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace realm;
|
||||
|
||||
#ifdef __has_cpp_attribute
|
||||
#define REALM_HAS_CCP_ATTRIBUTE(attr) __has_cpp_attribute(attr)
|
||||
#else
|
||||
#define REALM_HAS_CCP_ATTRIBUTE(attr) 0
|
||||
#endif
|
||||
|
||||
#if REALM_HAS_CCP_ATTRIBUTE(clang::fallthrough)
|
||||
#define REALM_FALLTHROUGH [[clang::fallthrough]]
|
||||
#else
|
||||
#define REALM_FALLTHROUGH
|
||||
#endif
|
||||
|
||||
Results::Results(SharedRealm r, Query q, SortOrder s)
|
||||
: m_realm(std::move(r))
|
||||
, m_query(std::move(q))
|
||||
, m_table(m_query.get_table().get())
|
||||
, m_sort(std::move(s))
|
||||
, m_mode(Mode::Query)
|
||||
{
|
||||
}
|
||||
|
||||
Results::Results(SharedRealm r, Table& table)
|
||||
: m_realm(std::move(r))
|
||||
, m_table(&table)
|
||||
, m_mode(Mode::Table)
|
||||
{
|
||||
}
|
||||
|
||||
Results::Results(SharedRealm r, SortOrder s, TableView tv)
|
||||
: m_realm(std::move(r))
|
||||
, m_table_view(std::move(tv))
|
||||
, m_table(&m_table_view.get_parent())
|
||||
, m_sort(std::move(s))
|
||||
, m_mode(Mode::TableView)
|
||||
{
|
||||
}
|
||||
|
||||
Results::~Results()
|
||||
{
|
||||
if (m_background_query) {
|
||||
m_background_query->unregister();
|
||||
}
|
||||
}
|
||||
|
||||
void Results::validate_read() const
|
||||
{
|
||||
if (m_realm)
|
||||
m_realm->verify_thread();
|
||||
if (m_table && !m_table->is_attached())
|
||||
throw InvalidatedException();
|
||||
if (m_mode == Mode::TableView && !m_table_view.is_attached())
|
||||
throw InvalidatedException();
|
||||
}
|
||||
|
||||
void Results::validate_write() const
|
||||
{
|
||||
validate_read();
|
||||
if (!m_realm || !m_realm->is_in_transaction())
|
||||
throw InvalidTransactionException("Must be in a write transaction");
|
||||
}
|
||||
|
||||
size_t Results::size()
|
||||
{
|
||||
validate_read();
|
||||
switch (m_mode) {
|
||||
case Mode::Empty: return 0;
|
||||
case Mode::Table: return m_table->size();
|
||||
case Mode::Query: return m_query.count();
|
||||
case Mode::TableView:
|
||||
update_tableview();
|
||||
return m_table_view.size();
|
||||
}
|
||||
REALM_UNREACHABLE();
|
||||
}
|
||||
|
||||
RowExpr Results::get(size_t row_ndx)
|
||||
{
|
||||
validate_read();
|
||||
switch (m_mode) {
|
||||
case Mode::Empty: break;
|
||||
case Mode::Table:
|
||||
if (row_ndx < m_table->size())
|
||||
return m_table->get(row_ndx);
|
||||
break;
|
||||
case Mode::Query:
|
||||
case Mode::TableView:
|
||||
update_tableview();
|
||||
if (row_ndx < m_table_view.size())
|
||||
return m_table_view.get(row_ndx);
|
||||
break;
|
||||
}
|
||||
|
||||
throw OutOfBoundsIndexException{row_ndx, size()};
|
||||
}
|
||||
|
||||
util::Optional<RowExpr> Results::first()
|
||||
{
|
||||
validate_read();
|
||||
switch (m_mode) {
|
||||
case Mode::Empty:
|
||||
return none;
|
||||
case Mode::Table:
|
||||
return m_table->size() == 0 ? util::none : util::make_optional(m_table->front());
|
||||
case Mode::Query:
|
||||
case Mode::TableView:
|
||||
update_tableview();
|
||||
return m_table_view.size() == 0 ? util::none : util::make_optional(m_table_view.front());
|
||||
}
|
||||
REALM_UNREACHABLE();
|
||||
}
|
||||
|
||||
util::Optional<RowExpr> Results::last()
|
||||
{
|
||||
validate_read();
|
||||
switch (m_mode) {
|
||||
case Mode::Empty:
|
||||
return none;
|
||||
case Mode::Table:
|
||||
return m_table->size() == 0 ? util::none : util::make_optional(m_table->back());
|
||||
case Mode::Query:
|
||||
case Mode::TableView:
|
||||
update_tableview();
|
||||
return m_table_view.size() == 0 ? util::none : util::make_optional(m_table_view.back());
|
||||
}
|
||||
REALM_UNREACHABLE();
|
||||
}
|
||||
|
||||
void Results::update_tableview()
|
||||
{
|
||||
validate_read();
|
||||
switch (m_mode) {
|
||||
case Mode::Empty:
|
||||
case Mode::Table:
|
||||
return;
|
||||
case Mode::Query:
|
||||
m_table_view = m_query.find_all();
|
||||
if (m_sort) {
|
||||
m_table_view.sort(m_sort.columnIndices, m_sort.ascending);
|
||||
}
|
||||
m_mode = Mode::TableView;
|
||||
break;
|
||||
case Mode::TableView:
|
||||
if (!m_background_query && !m_realm->is_in_transaction() && m_realm->can_deliver_notifications()) {
|
||||
m_background_query = std::make_shared<_impl::AsyncQuery>(*this);
|
||||
_impl::RealmCoordinator::register_query(m_background_query);
|
||||
}
|
||||
m_has_used_table_view = true;
|
||||
m_table_view.sync_if_needed();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t Results::index_of(Row const& row)
|
||||
{
|
||||
validate_read();
|
||||
if (!row) {
|
||||
throw DetatchedAccessorException{};
|
||||
}
|
||||
if (m_table && row.get_table() != m_table) {
|
||||
throw IncorrectTableException{
|
||||
ObjectStore::object_type_for_table_name(m_table->get_name()),
|
||||
ObjectStore::object_type_for_table_name(row.get_table()->get_name())};
|
||||
}
|
||||
return index_of(row.get_index());
|
||||
}
|
||||
|
||||
size_t Results::index_of(size_t row_ndx)
|
||||
{
|
||||
validate_read();
|
||||
switch (m_mode) {
|
||||
case Mode::Empty:
|
||||
return not_found;
|
||||
case Mode::Table:
|
||||
return row_ndx;
|
||||
case Mode::Query:
|
||||
case Mode::TableView:
|
||||
update_tableview();
|
||||
return m_table_view.find_by_source_ndx(row_ndx);
|
||||
}
|
||||
REALM_UNREACHABLE();
|
||||
}
|
||||
|
||||
template<typename Int, typename Float, typename Double, typename DateTime>
|
||||
util::Optional<Mixed> Results::aggregate(size_t column, bool return_none_for_empty,
|
||||
Int agg_int, Float agg_float,
|
||||
Double agg_double, DateTime agg_datetime)
|
||||
{
|
||||
validate_read();
|
||||
if (!m_table)
|
||||
return none;
|
||||
if (column > m_table->get_column_count())
|
||||
throw OutOfBoundsIndexException{column, m_table->get_column_count()};
|
||||
|
||||
auto do_agg = [&](auto const& getter) -> util::Optional<Mixed> {
|
||||
switch (m_mode) {
|
||||
case Mode::Empty:
|
||||
return none;
|
||||
case Mode::Table:
|
||||
if (return_none_for_empty && m_table->size() == 0)
|
||||
return none;
|
||||
return util::Optional<Mixed>(getter(*m_table));
|
||||
case Mode::Query:
|
||||
case Mode::TableView:
|
||||
this->update_tableview();
|
||||
if (return_none_for_empty && m_table_view.size() == 0)
|
||||
return none;
|
||||
return util::Optional<Mixed>(getter(m_table_view));
|
||||
}
|
||||
REALM_UNREACHABLE();
|
||||
};
|
||||
|
||||
switch (m_table->get_column_type(column))
|
||||
{
|
||||
case type_DateTime: return do_agg(agg_datetime);
|
||||
case type_Double: return do_agg(agg_double);
|
||||
case type_Float: return do_agg(agg_float);
|
||||
case type_Int: return do_agg(agg_int);
|
||||
default:
|
||||
throw UnsupportedColumnTypeException{column, m_table};
|
||||
}
|
||||
}
|
||||
|
||||
util::Optional<Mixed> Results::max(size_t column)
|
||||
{
|
||||
return aggregate(column, true,
|
||||
[=](auto const& table) { return table.maximum_int(column); },
|
||||
[=](auto const& table) { return table.maximum_float(column); },
|
||||
[=](auto const& table) { return table.maximum_double(column); },
|
||||
[=](auto const& table) { return table.maximum_datetime(column); });
|
||||
}
|
||||
|
||||
util::Optional<Mixed> Results::min(size_t column)
|
||||
{
|
||||
return aggregate(column, true,
|
||||
[=](auto const& table) { return table.minimum_int(column); },
|
||||
[=](auto const& table) { return table.minimum_float(column); },
|
||||
[=](auto const& table) { return table.minimum_double(column); },
|
||||
[=](auto const& table) { return table.minimum_datetime(column); });
|
||||
}
|
||||
|
||||
util::Optional<Mixed> Results::sum(size_t column)
|
||||
{
|
||||
return aggregate(column, false,
|
||||
[=](auto const& table) { return table.sum_int(column); },
|
||||
[=](auto const& table) { return table.sum_float(column); },
|
||||
[=](auto const& table) { return table.sum_double(column); },
|
||||
[=](auto const&) -> util::None { throw UnsupportedColumnTypeException{column, m_table}; });
|
||||
}
|
||||
|
||||
util::Optional<Mixed> Results::average(size_t column)
|
||||
{
|
||||
return aggregate(column, true,
|
||||
[=](auto const& table) { return table.average_int(column); },
|
||||
[=](auto const& table) { return table.average_float(column); },
|
||||
[=](auto const& table) { return table.average_double(column); },
|
||||
[=](auto const&) -> util::None { throw UnsupportedColumnTypeException{column, m_table}; });
|
||||
}
|
||||
|
||||
void Results::clear()
|
||||
{
|
||||
switch (m_mode) {
|
||||
case Mode::Empty:
|
||||
return;
|
||||
case Mode::Table:
|
||||
validate_write();
|
||||
m_table->clear();
|
||||
break;
|
||||
case Mode::Query:
|
||||
// Not using Query:remove() because building the tableview and
|
||||
// clearing it is actually significantly faster
|
||||
case Mode::TableView:
|
||||
validate_write();
|
||||
update_tableview();
|
||||
m_table_view.clear(RemoveMode::unordered);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Query Results::get_query() const
|
||||
{
|
||||
validate_read();
|
||||
switch (m_mode) {
|
||||
case Mode::Empty:
|
||||
case Mode::Query:
|
||||
return m_query;
|
||||
case Mode::TableView:
|
||||
return m_table_view.get_query();
|
||||
case Mode::Table:
|
||||
return m_table->where();
|
||||
}
|
||||
REALM_UNREACHABLE();
|
||||
}
|
||||
|
||||
TableView Results::get_tableview()
|
||||
{
|
||||
validate_read();
|
||||
switch (m_mode) {
|
||||
case Mode::Empty:
|
||||
return {};
|
||||
case Mode::Query:
|
||||
case Mode::TableView:
|
||||
update_tableview();
|
||||
return m_table_view;
|
||||
case Mode::Table:
|
||||
return m_table->where().find_all();
|
||||
}
|
||||
REALM_UNREACHABLE();
|
||||
}
|
||||
|
||||
StringData Results::get_object_type() const noexcept
|
||||
{
|
||||
return ObjectStore::object_type_for_table_name(m_table->get_name());
|
||||
}
|
||||
|
||||
Results Results::sort(realm::SortOrder&& sort) const
|
||||
{
|
||||
return Results(m_realm, get_query(), std::move(sort));
|
||||
}
|
||||
|
||||
Results Results::filter(Query&& q) const
|
||||
{
|
||||
return Results(m_realm, get_query().and_query(std::move(q)), get_sort());
|
||||
}
|
||||
|
||||
AsyncQueryCancelationToken Results::async(std::function<void (std::exception_ptr)> target)
|
||||
{
|
||||
if (m_realm->config().read_only) {
|
||||
throw InvalidTransactionException("Cannot create asynchronous query for read-only Realms");
|
||||
}
|
||||
if (m_realm->is_in_transaction()) {
|
||||
throw InvalidTransactionException("Cannot create asynchronous query while in a write transaction");
|
||||
}
|
||||
|
||||
if (!m_background_query) {
|
||||
m_background_query = std::make_shared<_impl::AsyncQuery>(*this);
|
||||
_impl::RealmCoordinator::register_query(m_background_query);
|
||||
}
|
||||
return {m_background_query, m_background_query->add_callback(std::move(target))};
|
||||
}
|
||||
|
||||
void Results::Internal::set_table_view(Results& results, realm::TableView &&tv)
|
||||
{
|
||||
// If the previous TableView was never actually used, then stop generating
|
||||
// new ones until the user actually uses the Results object again
|
||||
if (results.m_mode == Mode::TableView) {
|
||||
results.m_wants_background_updates = results.m_has_used_table_view;
|
||||
}
|
||||
|
||||
results.m_table_view = std::move(tv);
|
||||
results.m_mode = Mode::TableView;
|
||||
results.m_has_used_table_view = false;
|
||||
// needs https://github.com/realm/realm-core/pull/1392
|
||||
// REALM_ASSERT(results.m_table_view.is_in_sync());
|
||||
}
|
||||
|
||||
Results::UnsupportedColumnTypeException::UnsupportedColumnTypeException(size_t column, const Table* table)
|
||||
{
|
||||
column_index = column;
|
||||
column_name = table->get_column_name(column);
|
||||
column_type = table->get_column_type(column);
|
||||
}
|
||||
|
||||
AsyncQueryCancelationToken::AsyncQueryCancelationToken(std::shared_ptr<_impl::AsyncQuery> query, size_t token)
|
||||
: m_query(std::move(query)), m_token(token)
|
||||
{
|
||||
}
|
||||
|
||||
AsyncQueryCancelationToken::~AsyncQueryCancelationToken()
|
||||
{
|
||||
// m_query itself (and not just the pointed-to thing) needs to be accessed
|
||||
// atomically to ensure that there are no data races when the token is
|
||||
// destroyed after being modified on a different thread.
|
||||
// This is needed despite the token not being thread-safe in general as
|
||||
// users find it very surpringing for obj-c objects to care about what
|
||||
// thread they are deallocated on.
|
||||
if (auto query = std::atomic_load(&m_query)) {
|
||||
query->remove_callback(m_token);
|
||||
}
|
||||
}
|
||||
|
||||
AsyncQueryCancelationToken::AsyncQueryCancelationToken(AsyncQueryCancelationToken&& rgt)
|
||||
: m_query(std::atomic_exchange(&rgt.m_query, {})), m_token(rgt.m_token)
|
||||
{
|
||||
}
|
||||
|
||||
AsyncQueryCancelationToken& AsyncQueryCancelationToken::operator=(realm::AsyncQueryCancelationToken&& rgt)
|
||||
{
|
||||
if (this != &rgt) {
|
||||
if (auto query = std::atomic_load(&m_query)) {
|
||||
query->remove_callback(m_token);
|
||||
}
|
||||
std::atomic_store(&m_query, std::atomic_exchange(&rgt.m_query, {}));
|
||||
m_token = rgt.m_token;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
102
Example/Pods/Realm/Realm/ObjectStore/schema.cpp
generated
Normal file
102
Example/Pods/Realm/Realm/ObjectStore/schema.cpp
generated
Normal file
@@ -0,0 +1,102 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2015 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.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "schema.hpp"
|
||||
|
||||
#include "object_schema.hpp"
|
||||
#include "object_store.hpp"
|
||||
#include "property.hpp"
|
||||
|
||||
using namespace realm;
|
||||
|
||||
static bool compare_by_name(ObjectSchema const& lft, ObjectSchema const& rgt) {
|
||||
return lft.name < rgt.name;
|
||||
}
|
||||
|
||||
Schema::Schema(base types) : base(std::move(types)) {
|
||||
std::sort(begin(), end(), compare_by_name);
|
||||
}
|
||||
|
||||
Schema::iterator Schema::find(std::string const& name)
|
||||
{
|
||||
ObjectSchema cmp;
|
||||
cmp.name = name;
|
||||
return find(cmp);
|
||||
}
|
||||
|
||||
Schema::const_iterator Schema::find(std::string const& name) const
|
||||
{
|
||||
return const_cast<Schema *>(this)->find(name);
|
||||
}
|
||||
|
||||
Schema::iterator Schema::find(ObjectSchema const& object) noexcept
|
||||
{
|
||||
auto it = std::lower_bound(begin(), end(), object, compare_by_name);
|
||||
if (it != end() && it->name != object.name) {
|
||||
it = end();
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
Schema::const_iterator Schema::find(ObjectSchema const& object) const noexcept
|
||||
{
|
||||
return const_cast<Schema *>(this)->find(object);
|
||||
}
|
||||
|
||||
void Schema::validate() const
|
||||
{
|
||||
std::vector<ObjectSchemaValidationException> exceptions;
|
||||
for (auto const& object : *this) {
|
||||
const Property *primary = nullptr;
|
||||
for (auto const& prop : object.properties) {
|
||||
// check object_type existence
|
||||
if (!prop.object_type.empty() && find(prop.object_type) == end()) {
|
||||
exceptions.emplace_back(MissingObjectTypeException(object.name, prop));
|
||||
}
|
||||
|
||||
// check nullablity
|
||||
if (prop.is_nullable) {
|
||||
if (prop.type == PropertyTypeArray || prop.type == PropertyTypeAny) {
|
||||
exceptions.emplace_back(InvalidNullabilityException(object.name, prop));
|
||||
}
|
||||
}
|
||||
else if (prop.type == PropertyTypeObject) {
|
||||
exceptions.emplace_back(InvalidNullabilityException(object.name, prop));
|
||||
}
|
||||
|
||||
// check primary keys
|
||||
if (prop.is_primary) {
|
||||
if (primary) {
|
||||
exceptions.emplace_back(DuplicatePrimaryKeysException(object.name));
|
||||
}
|
||||
primary = ∝
|
||||
}
|
||||
|
||||
// check indexable
|
||||
if (prop.is_indexed) {
|
||||
if (!prop.is_indexable()) {
|
||||
exceptions.emplace_back(PropertyTypeNotIndexableException(object.name, prop));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exceptions.size()) {
|
||||
throw SchemaValidationException(exceptions);
|
||||
}
|
||||
}
|
||||
457
Example/Pods/Realm/Realm/ObjectStore/shared_realm.cpp
generated
Normal file
457
Example/Pods/Realm/Realm/ObjectStore/shared_realm.cpp
generated
Normal file
@@ -0,0 +1,457 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2015 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.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "shared_realm.hpp"
|
||||
|
||||
#include "binding_context.hpp"
|
||||
#include "impl/external_commit_helper.hpp"
|
||||
#include "impl/realm_coordinator.hpp"
|
||||
#include "impl/transact_log_handler.hpp"
|
||||
#include "object_store.hpp"
|
||||
#include "schema.hpp"
|
||||
|
||||
#include <realm/commit_log.hpp>
|
||||
#include <realm/group_shared.hpp>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
using namespace realm;
|
||||
using namespace realm::_impl;
|
||||
|
||||
Realm::Config::Config(const Config& c)
|
||||
: path(c.path)
|
||||
, read_only(c.read_only)
|
||||
, in_memory(c.in_memory)
|
||||
, cache(c.cache)
|
||||
, disable_format_upgrade(c.disable_format_upgrade)
|
||||
, encryption_key(c.encryption_key)
|
||||
, schema_version(c.schema_version)
|
||||
, migration_function(c.migration_function)
|
||||
{
|
||||
if (c.schema) {
|
||||
schema = std::make_unique<Schema>(*c.schema);
|
||||
}
|
||||
}
|
||||
|
||||
Realm::Config::Config() : schema_version(ObjectStore::NotVersioned) { }
|
||||
Realm::Config::Config(Config&&) = default;
|
||||
Realm::Config::~Config() = default;
|
||||
|
||||
Realm::Config& Realm::Config::operator=(realm::Realm::Config const& c)
|
||||
{
|
||||
if (&c != this) {
|
||||
*this = Config(c);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Realm::Realm(Config config)
|
||||
: m_config(std::move(config))
|
||||
{
|
||||
open_with_config(m_config, m_history, m_shared_group, m_read_only_group);
|
||||
|
||||
if (m_read_only_group) {
|
||||
m_group = m_read_only_group.get();
|
||||
}
|
||||
}
|
||||
|
||||
void Realm::open_with_config(const Config& config,
|
||||
std::unique_ptr<Replication>& history,
|
||||
std::unique_ptr<SharedGroup>& shared_group,
|
||||
std::unique_ptr<Group>& read_only_group)
|
||||
{
|
||||
try {
|
||||
if (config.read_only) {
|
||||
read_only_group = std::make_unique<Group>(config.path, config.encryption_key.data(), Group::mode_ReadOnly);
|
||||
}
|
||||
else {
|
||||
history = realm::make_client_history(config.path, config.encryption_key.data());
|
||||
SharedGroup::DurabilityLevel durability = config.in_memory ? SharedGroup::durability_MemOnly :
|
||||
SharedGroup::durability_Full;
|
||||
shared_group = std::make_unique<SharedGroup>(*history, durability, config.encryption_key.data(), !config.disable_format_upgrade);
|
||||
}
|
||||
}
|
||||
catch (util::File::PermissionDenied const& ex) {
|
||||
throw RealmFileException(RealmFileException::Kind::PermissionDenied, ex.get_path(),
|
||||
"Unable to open a realm at path '" + ex.get_path() +
|
||||
"'. Please use a path where your app has " + (config.read_only ? "read" : "read-write") + " permissions.");
|
||||
}
|
||||
catch (util::File::Exists const& ex) {
|
||||
throw RealmFileException(RealmFileException::Kind::Exists, ex.get_path(),
|
||||
"File at path '" + ex.get_path() + "' already exists.");
|
||||
}
|
||||
catch (util::File::NotFound const& ex) {
|
||||
throw RealmFileException(RealmFileException::Kind::NotFound, ex.get_path(),
|
||||
"File at path '" + ex.get_path() + "' does not exist.");
|
||||
}
|
||||
catch (util::File::AccessError const& ex) {
|
||||
throw RealmFileException(RealmFileException::Kind::AccessError, ex.get_path(),
|
||||
"Unable to open a realm at path '" + ex.get_path() + "'");
|
||||
}
|
||||
catch (IncompatibleLockFile const& ex) {
|
||||
throw RealmFileException(RealmFileException::Kind::IncompatibleLockFile, config.path,
|
||||
"Realm file is currently open in another process "
|
||||
"which cannot share access with this process. All processes sharing a single file must be the same architecture.");
|
||||
}
|
||||
catch (FileFormatUpgradeRequired const& ex) {
|
||||
throw RealmFileException(RealmFileException::Kind::FormatUpgradeRequired, config.path,
|
||||
"The Realm file format must be allowed to be upgraded "
|
||||
"in order to proceed.");
|
||||
}
|
||||
}
|
||||
|
||||
void Realm::init(std::shared_ptr<RealmCoordinator> coordinator)
|
||||
{
|
||||
m_coordinator = std::move(coordinator);
|
||||
|
||||
// if there is an existing realm at the current path steal its schema/column mapping
|
||||
if (auto existing = m_coordinator->get_schema()) {
|
||||
m_config.schema = std::make_unique<Schema>(*existing);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// otherwise get the schema from the group
|
||||
auto target_schema = std::move(m_config.schema);
|
||||
auto target_schema_version = m_config.schema_version;
|
||||
m_config.schema_version = ObjectStore::get_schema_version(read_group());
|
||||
m_config.schema = std::make_unique<Schema>(ObjectStore::schema_from_group(read_group()));
|
||||
|
||||
// if a target schema is supplied, verify that it matches or migrate to
|
||||
// it, as neeeded
|
||||
if (target_schema) {
|
||||
if (m_config.read_only) {
|
||||
if (m_config.schema_version == ObjectStore::NotVersioned) {
|
||||
throw UnitializedRealmException("Can't open an un-initialized Realm without a Schema");
|
||||
}
|
||||
target_schema->validate();
|
||||
ObjectStore::verify_schema(*m_config.schema, *target_schema, true);
|
||||
m_config.schema = std::move(target_schema);
|
||||
}
|
||||
else {
|
||||
update_schema(std::move(target_schema), target_schema_version);
|
||||
}
|
||||
|
||||
if (!m_config.read_only) {
|
||||
// End the read transaction created to validation/update the
|
||||
// schema to avoid pinning the version even if the user never
|
||||
// actually reads data
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
// Trying to unregister from the coordinator before we finish
|
||||
// construction will result in a deadlock
|
||||
m_coordinator = nullptr;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
Realm::~Realm()
|
||||
{
|
||||
if (m_coordinator) {
|
||||
m_coordinator->unregister_realm(this);
|
||||
}
|
||||
}
|
||||
|
||||
Group *Realm::read_group()
|
||||
{
|
||||
if (!m_group) {
|
||||
m_group = &const_cast<Group&>(m_shared_group->begin_read());
|
||||
}
|
||||
return m_group;
|
||||
}
|
||||
|
||||
SharedRealm Realm::get_shared_realm(Config config)
|
||||
{
|
||||
return RealmCoordinator::get_coordinator(config.path)->get_realm(std::move(config));
|
||||
}
|
||||
|
||||
void Realm::update_schema(std::unique_ptr<Schema> schema, uint64_t version)
|
||||
{
|
||||
schema->validate();
|
||||
|
||||
auto needs_update = [&] {
|
||||
// If the schema version matches, just verify that the schema itself also matches
|
||||
bool needs_write = !m_config.read_only && (m_config.schema_version != version || ObjectStore::needs_update(*m_config.schema, *schema));
|
||||
if (needs_write) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ObjectStore::verify_schema(*m_config.schema, *schema, m_config.read_only);
|
||||
m_config.schema = std::move(schema);
|
||||
m_config.schema_version = version;
|
||||
m_coordinator->update_schema(*m_config.schema);
|
||||
return false;
|
||||
};
|
||||
|
||||
if (!needs_update()) {
|
||||
return;
|
||||
}
|
||||
|
||||
read_group();
|
||||
transaction::begin(*m_shared_group, m_binding_context.get(),
|
||||
/* error on schema changes */ false);
|
||||
|
||||
struct WriteTransactionGuard {
|
||||
Realm& realm;
|
||||
~WriteTransactionGuard() {
|
||||
if (realm.is_in_transaction()) {
|
||||
realm.cancel_transaction();
|
||||
}
|
||||
}
|
||||
} write_transaction_guard{*this};
|
||||
|
||||
// Recheck the schema version after beginning the write transaction
|
||||
// If it changed then someone else initialized the schema and we need to
|
||||
// recheck everything
|
||||
auto current_schema_version = ObjectStore::get_schema_version(read_group());
|
||||
if (current_schema_version != m_config.schema_version) {
|
||||
m_config.schema_version = current_schema_version;
|
||||
*m_config.schema = ObjectStore::schema_from_group(read_group());
|
||||
|
||||
if (!needs_update()) {
|
||||
cancel_transaction();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Config old_config(m_config);
|
||||
auto migration_function = [&](Group*, Schema&) {
|
||||
SharedRealm old_realm(new Realm(old_config));
|
||||
// Need to open in read-write mode so that it uses a SharedGroup, but
|
||||
// users shouldn't actually be able to write via the old realm
|
||||
old_realm->m_config.read_only = true;
|
||||
|
||||
if (m_config.migration_function) {
|
||||
m_config.migration_function(old_realm, shared_from_this());
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
m_config.schema = std::move(schema);
|
||||
m_config.schema_version = version;
|
||||
|
||||
ObjectStore::update_realm_with_schema(read_group(), *old_config.schema,
|
||||
version, *m_config.schema,
|
||||
migration_function);
|
||||
commit_transaction();
|
||||
}
|
||||
catch (...) {
|
||||
m_config.schema = std::move(old_config.schema);
|
||||
m_config.schema_version = old_config.schema_version;
|
||||
throw;
|
||||
}
|
||||
|
||||
m_coordinator->update_schema(*m_config.schema);
|
||||
}
|
||||
|
||||
static void check_read_write(Realm *realm)
|
||||
{
|
||||
if (realm->config().read_only) {
|
||||
throw InvalidTransactionException("Can't perform transactions on read-only Realms.");
|
||||
}
|
||||
}
|
||||
|
||||
void Realm::verify_thread() const
|
||||
{
|
||||
if (m_thread_id != std::this_thread::get_id()) {
|
||||
throw IncorrectThreadException();
|
||||
}
|
||||
}
|
||||
|
||||
void Realm::verify_in_write() const
|
||||
{
|
||||
if (!is_in_transaction()) {
|
||||
throw InvalidTransactionException("Cannot modify persisted objects outside of a write transaction.");
|
||||
}
|
||||
}
|
||||
|
||||
bool Realm::is_in_transaction() const noexcept
|
||||
{
|
||||
if (!m_shared_group) {
|
||||
return false;
|
||||
}
|
||||
return m_shared_group->get_transact_stage() == SharedGroup::transact_Writing;
|
||||
}
|
||||
|
||||
void Realm::begin_transaction()
|
||||
{
|
||||
check_read_write(this);
|
||||
verify_thread();
|
||||
|
||||
if (is_in_transaction()) {
|
||||
throw InvalidTransactionException("The Realm is already in a write transaction");
|
||||
}
|
||||
|
||||
// make sure we have a read transaction
|
||||
read_group();
|
||||
|
||||
transaction::begin(*m_shared_group, m_binding_context.get());
|
||||
}
|
||||
|
||||
void Realm::commit_transaction()
|
||||
{
|
||||
check_read_write(this);
|
||||
verify_thread();
|
||||
|
||||
if (!is_in_transaction()) {
|
||||
throw InvalidTransactionException("Can't commit a non-existing write transaction");
|
||||
}
|
||||
|
||||
transaction::commit(*m_shared_group, m_binding_context.get());
|
||||
m_coordinator->send_commit_notifications();
|
||||
}
|
||||
|
||||
void Realm::cancel_transaction()
|
||||
{
|
||||
check_read_write(this);
|
||||
verify_thread();
|
||||
|
||||
if (!is_in_transaction()) {
|
||||
throw InvalidTransactionException("Can't cancel a non-existing write transaction");
|
||||
}
|
||||
|
||||
transaction::cancel(*m_shared_group, m_binding_context.get());
|
||||
}
|
||||
|
||||
void Realm::invalidate()
|
||||
{
|
||||
verify_thread();
|
||||
check_read_write(this);
|
||||
|
||||
if (is_in_transaction()) {
|
||||
cancel_transaction();
|
||||
}
|
||||
if (!m_group) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_shared_group->end_read();
|
||||
m_group = nullptr;
|
||||
}
|
||||
|
||||
bool Realm::compact()
|
||||
{
|
||||
verify_thread();
|
||||
|
||||
if (m_config.read_only) {
|
||||
throw InvalidTransactionException("Can't compact a read-only Realm");
|
||||
}
|
||||
if (is_in_transaction()) {
|
||||
throw InvalidTransactionException("Can't compact a Realm within a write transaction");
|
||||
}
|
||||
|
||||
Group* group = read_group();
|
||||
for (auto &object_schema : *m_config.schema) {
|
||||
ObjectStore::table_for_object_type(group, object_schema.name)->optimize();
|
||||
}
|
||||
m_shared_group->end_read();
|
||||
m_group = nullptr;
|
||||
|
||||
return m_shared_group->compact();
|
||||
}
|
||||
|
||||
void Realm::notify()
|
||||
{
|
||||
verify_thread();
|
||||
|
||||
if (m_shared_group->has_changed()) { // Throws
|
||||
if (m_binding_context) {
|
||||
m_binding_context->changes_available();
|
||||
}
|
||||
if (m_auto_refresh) {
|
||||
if (m_group) {
|
||||
m_coordinator->advance_to_ready(*this);
|
||||
}
|
||||
else if (m_binding_context) {
|
||||
m_binding_context->did_change({}, {});
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_coordinator->process_available_async(*this);
|
||||
}
|
||||
}
|
||||
|
||||
bool Realm::refresh()
|
||||
{
|
||||
verify_thread();
|
||||
check_read_write(this);
|
||||
|
||||
// can't be any new changes if we're in a write transaction
|
||||
if (is_in_transaction()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// advance transaction if database has changed
|
||||
if (!m_shared_group->has_changed()) { // Throws
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_group) {
|
||||
transaction::advance(*m_shared_group, m_binding_context.get());
|
||||
m_coordinator->process_available_async(*this);
|
||||
}
|
||||
else {
|
||||
// Create the read transaction
|
||||
read_group();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Realm::can_deliver_notifications() const noexcept
|
||||
{
|
||||
if (m_config.read_only) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_binding_context && !m_binding_context->can_deliver_notifications()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t Realm::get_schema_version(const realm::Realm::Config &config)
|
||||
{
|
||||
auto coordinator = RealmCoordinator::get_existing_coordinator(config.path);
|
||||
if (coordinator) {
|
||||
return coordinator->get_schema_version();
|
||||
}
|
||||
|
||||
return ObjectStore::get_schema_version(Realm(config).read_group());
|
||||
}
|
||||
|
||||
void Realm::close()
|
||||
{
|
||||
invalidate();
|
||||
|
||||
if (m_coordinator) {
|
||||
m_coordinator->unregister_realm(this);
|
||||
}
|
||||
|
||||
m_group = nullptr;
|
||||
m_shared_group = nullptr;
|
||||
m_history = nullptr;
|
||||
m_read_only_group = nullptr;
|
||||
m_binding_context = nullptr;
|
||||
m_coordinator = nullptr;
|
||||
}
|
||||
909
Example/Pods/Realm/Realm/RLMAccessor.mm
generated
Normal file
909
Example/Pods/Realm/Realm/RLMAccessor.mm
generated
Normal file
@@ -0,0 +1,909 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RLMAccessor.h"
|
||||
|
||||
#import "RLMArray_Private.hpp"
|
||||
#import "RLMObservation.hpp"
|
||||
#import "RLMObjectSchema_Private.hpp"
|
||||
#import "RLMObjectStore.h"
|
||||
#import "RLMObject_Private.hpp"
|
||||
#import "RLMProperty_Private.h"
|
||||
#import "RLMRealm_Private.hpp"
|
||||
#import "RLMSchema_Private.h"
|
||||
#import "RLMUtil.hpp"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
#import <realm/descriptor.hpp>
|
||||
|
||||
typedef NS_ENUM(char, RLMAccessorCode) {
|
||||
RLMAccessorCodeByte,
|
||||
RLMAccessorCodeShort,
|
||||
RLMAccessorCodeInt,
|
||||
RLMAccessorCodeLong,
|
||||
RLMAccessorCodeLongLong,
|
||||
RLMAccessorCodeFloat,
|
||||
RLMAccessorCodeDouble,
|
||||
RLMAccessorCodeBool,
|
||||
RLMAccessorCodeString,
|
||||
RLMAccessorCodeDate,
|
||||
RLMAccessorCodeData,
|
||||
RLMAccessorCodeLink,
|
||||
RLMAccessorCodeArray,
|
||||
RLMAccessorCodeAny,
|
||||
|
||||
RLMAccessorCodeIntObject,
|
||||
RLMAccessorCodeFloatObject,
|
||||
RLMAccessorCodeDoubleObject,
|
||||
RLMAccessorCodeBoolObject,
|
||||
};
|
||||
|
||||
// long getter/setter
|
||||
static inline long long RLMGetLong(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) {
|
||||
RLMVerifyAttached(obj);
|
||||
return obj->_row.get_int(colIndex);
|
||||
}
|
||||
static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, long long val) {
|
||||
RLMVerifyInWriteTransaction(obj);
|
||||
obj->_row.set_int(colIndex, val);
|
||||
}
|
||||
static inline void RLMSetValueUnique(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, NSString *propName, long long val) {
|
||||
RLMVerifyInWriteTransaction(obj);
|
||||
size_t row = obj->_row.get_table()->find_first_int(colIndex, val);
|
||||
if (row == obj->_row.get_index()) {
|
||||
return;
|
||||
}
|
||||
if (row != realm::not_found) {
|
||||
@throw RLMException(@"Can't set primary key property '%@' to existing value '%lld'.", propName, val);
|
||||
}
|
||||
obj->_row.set_int(colIndex, val);
|
||||
}
|
||||
|
||||
// float getter/setter
|
||||
static inline float RLMGetFloat(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) {
|
||||
RLMVerifyAttached(obj);
|
||||
return obj->_row.get_float(colIndex);
|
||||
}
|
||||
static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, float val) {
|
||||
RLMVerifyInWriteTransaction(obj);
|
||||
obj->_row.set_float(colIndex, val);
|
||||
}
|
||||
|
||||
// double getter/setter
|
||||
static inline double RLMGetDouble(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) {
|
||||
RLMVerifyAttached(obj);
|
||||
return obj->_row.get_double(colIndex);
|
||||
}
|
||||
static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, double val) {
|
||||
RLMVerifyInWriteTransaction(obj);
|
||||
obj->_row.set_double(colIndex, val);
|
||||
}
|
||||
|
||||
// bool getter/setter
|
||||
static inline bool RLMGetBool(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) {
|
||||
RLMVerifyAttached(obj);
|
||||
return obj->_row.get_bool(colIndex);
|
||||
}
|
||||
static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, BOOL val) {
|
||||
RLMVerifyInWriteTransaction(obj);
|
||||
obj->_row.set_bool(colIndex, val);
|
||||
}
|
||||
|
||||
// string getter/setter
|
||||
static inline NSString *RLMGetString(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) {
|
||||
RLMVerifyAttached(obj);
|
||||
return RLMStringDataToNSString(obj->_row.get_string(colIndex));
|
||||
}
|
||||
static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, __unsafe_unretained NSString *const val) {
|
||||
RLMVerifyInWriteTransaction(obj);
|
||||
try {
|
||||
obj->_row.set_string(colIndex, RLMStringDataWithNSString(val));
|
||||
}
|
||||
catch (std::exception const& e) {
|
||||
@throw RLMException(e);
|
||||
}
|
||||
}
|
||||
static inline void RLMSetValueUnique(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, NSString *propName,
|
||||
__unsafe_unretained NSString *const val) {
|
||||
RLMVerifyInWriteTransaction(obj);
|
||||
realm::StringData str = RLMStringDataWithNSString(val);
|
||||
size_t row = obj->_row.get_table()->find_first_string(colIndex, str);
|
||||
if (row == obj->_row.get_index()) {
|
||||
return;
|
||||
}
|
||||
if (row != realm::not_found) {
|
||||
@throw RLMException(@"Can't set primary key property '%@' to existing value '%@'.", propName, val);
|
||||
}
|
||||
try {
|
||||
obj->_row.set_string(colIndex, str);
|
||||
}
|
||||
catch (std::exception const& e) {
|
||||
@throw RLMException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// date getter/setter
|
||||
static inline NSDate *RLMGetDate(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) {
|
||||
RLMVerifyAttached(obj);
|
||||
if (obj->_row.is_null(colIndex)) {
|
||||
return nil;
|
||||
}
|
||||
realm::DateTime dt = obj->_row.get_datetime(colIndex);
|
||||
return RLMDateTimeToNSDate(dt);
|
||||
}
|
||||
static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, __unsafe_unretained NSDate *const date) {
|
||||
RLMVerifyInWriteTransaction(obj);
|
||||
if (date) {
|
||||
realm::DateTime dt = RLMDateTimeForNSDate(date);
|
||||
obj->_row.set_datetime(colIndex, dt);
|
||||
}
|
||||
else {
|
||||
obj->_row.set_null(colIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// data getter/setter
|
||||
static inline NSData *RLMGetData(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) {
|
||||
RLMVerifyAttached(obj);
|
||||
realm::BinaryData data = obj->_row.get_binary(colIndex);
|
||||
return RLMBinaryDataToNSData(data);
|
||||
}
|
||||
static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, __unsafe_unretained NSData *const data) {
|
||||
RLMVerifyInWriteTransaction(obj);
|
||||
|
||||
try {
|
||||
obj->_row.set_binary(colIndex, RLMBinaryDataForNSData(data));
|
||||
}
|
||||
catch (std::exception const& e) {
|
||||
@throw RLMException(e);
|
||||
}
|
||||
}
|
||||
|
||||
static inline RLMObjectBase *RLMGetLinkedObjectForValue(__unsafe_unretained RLMRealm *const realm,
|
||||
__unsafe_unretained NSString *const className,
|
||||
__unsafe_unretained id const value,
|
||||
RLMCreationOptions creationOptions) NS_RETURNS_RETAINED;
|
||||
static inline RLMObjectBase *RLMGetLinkedObjectForValue(__unsafe_unretained RLMRealm *const realm,
|
||||
__unsafe_unretained NSString *const className,
|
||||
__unsafe_unretained id const value,
|
||||
RLMCreationOptions creationOptions) {
|
||||
RLMObjectBase *link = RLMDynamicCast<RLMObjectBase>(value);
|
||||
if (!link || ![link->_objectSchema.className isEqualToString:className]) {
|
||||
// create from non-rlmobject
|
||||
return RLMCreateObjectInRealmWithValue(realm, className, value, creationOptions & RLMCreationOptionsCreateOrUpdate);
|
||||
}
|
||||
|
||||
if (link.isInvalidated) {
|
||||
@throw RLMException(@"Adding a deleted or invalidated object to a Realm is not permitted");
|
||||
}
|
||||
|
||||
if (link->_realm == realm) {
|
||||
return link;
|
||||
}
|
||||
|
||||
if (creationOptions & RLMCreationOptionsPromoteStandalone) {
|
||||
if (!link->_realm) {
|
||||
RLMAddObjectToRealm(link, realm, creationOptions & RLMCreationOptionsCreateOrUpdate);
|
||||
return link;
|
||||
}
|
||||
@throw RLMException(@"Can not add objects from a different Realm");
|
||||
}
|
||||
|
||||
// copy from another realm or copy from standalone
|
||||
return RLMCreateObjectInRealmWithValue(realm, className, link, creationOptions & RLMCreationOptionsCreateOrUpdate);
|
||||
}
|
||||
|
||||
// link getter/setter
|
||||
static inline RLMObjectBase *RLMGetLink(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, __unsafe_unretained NSString *const objectClassName) {
|
||||
RLMVerifyAttached(obj);
|
||||
|
||||
if (obj->_row.is_null_link(colIndex)) {
|
||||
return nil;
|
||||
}
|
||||
NSUInteger index = obj->_row.get_link(colIndex);
|
||||
return RLMCreateObjectAccessor(obj->_realm, obj->_realm.schema[objectClassName], index);
|
||||
}
|
||||
|
||||
static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
|
||||
__unsafe_unretained RLMObjectBase *const val) {
|
||||
RLMVerifyInWriteTransaction(obj);
|
||||
|
||||
if (!val) {
|
||||
obj->_row.nullify_link(colIndex);
|
||||
}
|
||||
else {
|
||||
// make sure it is the correct type
|
||||
RLMObjectSchema *valSchema = val->_objectSchema;
|
||||
RLMObjectSchema *objSchema = obj->_objectSchema;
|
||||
if (![[objSchema.properties[colIndex] objectClassName] isEqualToString:valSchema.className]) {
|
||||
@throw RLMException(@"Can't set object of type '%@' to property of type '%@'",
|
||||
valSchema.className, [objSchema.properties[colIndex] objectClassName]);
|
||||
}
|
||||
RLMObjectBase *link = RLMGetLinkedObjectForValue(obj->_realm, valSchema.className, val, RLMCreationOptionsPromoteStandalone);
|
||||
obj->_row.set_link(colIndex, link->_row.get_index());
|
||||
}
|
||||
}
|
||||
|
||||
// array getter/setter
|
||||
static inline RLMArray *RLMGetArray(__unsafe_unretained RLMObjectBase *const obj,
|
||||
NSUInteger colIndex,
|
||||
__unsafe_unretained NSString *const objectClassName,
|
||||
__unsafe_unretained NSString *const propName) {
|
||||
RLMVerifyAttached(obj);
|
||||
|
||||
realm::LinkViewRef linkView = obj->_row.get_linklist(colIndex);
|
||||
return [RLMArrayLinkView arrayWithObjectClassName:objectClassName
|
||||
view:linkView
|
||||
realm:obj->_realm
|
||||
key:propName
|
||||
parentSchema:obj->_objectSchema];
|
||||
}
|
||||
|
||||
static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
|
||||
__unsafe_unretained id<NSFastEnumeration> const array) {
|
||||
RLMVerifyInWriteTransaction(obj);
|
||||
|
||||
realm::LinkViewRef linkView = obj->_row.get_linklist(colIndex);
|
||||
// remove all old
|
||||
// FIXME: make sure delete rules don't purge objects
|
||||
linkView->clear();
|
||||
for (RLMObjectBase *link in array) {
|
||||
RLMObjectBase * addedLink = RLMGetLinkedObjectForValue(obj->_realm, link->_objectSchema.className, link, RLMCreationOptionsPromoteStandalone);
|
||||
linkView->add(addedLink->_row.get_index());
|
||||
}
|
||||
}
|
||||
|
||||
static inline NSNumber<RLMInt> *RLMGetIntObject(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) {
|
||||
RLMVerifyAttached(obj);
|
||||
|
||||
if (obj->_row.is_null(colIndex)) {
|
||||
return nil;
|
||||
}
|
||||
return @(obj->_row.get_int(colIndex));
|
||||
}
|
||||
static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
|
||||
__unsafe_unretained NSNumber<RLMInt> *const intObject) {
|
||||
RLMVerifyInWriteTransaction(obj);
|
||||
|
||||
if (intObject) {
|
||||
obj->_row.set_int(colIndex, intObject.longLongValue);
|
||||
}
|
||||
else {
|
||||
obj->_row.set_null(colIndex);
|
||||
}
|
||||
}
|
||||
static inline void RLMSetValueUnique(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, NSString *propName,
|
||||
__unsafe_unretained NSNumber<RLMInt> *const intObject) {
|
||||
RLMVerifyInWriteTransaction(obj);
|
||||
|
||||
long long longLongValue = 0;
|
||||
size_t row;
|
||||
if (intObject) {
|
||||
longLongValue = intObject.longLongValue;
|
||||
row = obj->_row.get_table()->find_first_int(colIndex, longLongValue);
|
||||
}
|
||||
else {
|
||||
row = obj->_row.get_table()->find_first_null(colIndex);
|
||||
}
|
||||
|
||||
if (row == obj->_row.get_index()) {
|
||||
return;
|
||||
}
|
||||
if (row != realm::not_found) {
|
||||
@throw RLMException(@"Can't set primary key property '%@' to existing value '%@'.", propName, intObject);
|
||||
}
|
||||
|
||||
if (intObject) {
|
||||
obj->_row.set_int(colIndex, longLongValue);
|
||||
}
|
||||
else {
|
||||
obj->_row.set_null(colIndex);
|
||||
}
|
||||
}
|
||||
|
||||
static inline NSNumber<RLMFloat> *RLMGetFloatObject(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) {
|
||||
RLMVerifyAttached(obj);
|
||||
|
||||
if (obj->_row.is_null(colIndex)) {
|
||||
return nil;
|
||||
}
|
||||
return @(obj->_row.get_float(colIndex));
|
||||
}
|
||||
static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
|
||||
__unsafe_unretained NSNumber<RLMFloat> *const floatObject) {
|
||||
RLMVerifyInWriteTransaction(obj);
|
||||
|
||||
if (floatObject) {
|
||||
obj->_row.set_float(colIndex, floatObject.floatValue);
|
||||
}
|
||||
else {
|
||||
obj->_row.set_null(colIndex);
|
||||
}
|
||||
}
|
||||
|
||||
static inline NSNumber<RLMDouble> *RLMGetDoubleObject(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) {
|
||||
RLMVerifyAttached(obj);
|
||||
|
||||
if (obj->_row.is_null(colIndex)) {
|
||||
return nil;
|
||||
}
|
||||
return @(obj->_row.get_double(colIndex));
|
||||
}
|
||||
static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
|
||||
__unsafe_unretained NSNumber<RLMDouble> *const doubleObject) {
|
||||
RLMVerifyInWriteTransaction(obj);
|
||||
|
||||
if (doubleObject) {
|
||||
obj->_row.set_double(colIndex, doubleObject.doubleValue);
|
||||
}
|
||||
else {
|
||||
obj->_row.set_null(colIndex);
|
||||
}
|
||||
}
|
||||
|
||||
static inline NSNumber<RLMBool> *RLMGetBoolObject(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) {
|
||||
RLMVerifyAttached(obj);
|
||||
|
||||
if (obj->_row.is_null(colIndex)) {
|
||||
return nil;
|
||||
}
|
||||
return @(obj->_row.get_bool(colIndex));
|
||||
}
|
||||
static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
|
||||
__unsafe_unretained NSNumber<RLMBool> *const boolObject) {
|
||||
RLMVerifyInWriteTransaction(obj);
|
||||
|
||||
if (boolObject) {
|
||||
obj->_row.set_bool(colIndex, boolObject.boolValue);
|
||||
}
|
||||
else {
|
||||
obj->_row.set_null(colIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// any getter/setter
|
||||
static inline id RLMGetAnyProperty(__unsafe_unretained RLMObjectBase *const obj, NSUInteger col_ndx) {
|
||||
RLMVerifyAttached(obj);
|
||||
return RLMMixedToObjc(obj->_row.get_mixed(col_ndx));
|
||||
}
|
||||
static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger col_ndx, __unsafe_unretained id val) {
|
||||
RLMVerifyInWriteTransaction(obj);
|
||||
|
||||
// FIXME - enable when Any supports links
|
||||
// if (obj == nil) {
|
||||
// table.nullify_link(col_ndx, row_ndx);
|
||||
// return;
|
||||
// }
|
||||
if (NSString *str = RLMDynamicCast<NSString>(val)) {
|
||||
obj->_row.set_mixed(col_ndx, RLMStringDataWithNSString(str));
|
||||
return;
|
||||
}
|
||||
if (NSDate *date = RLMDynamicCast<NSDate>(val)) {
|
||||
obj->_row.set_mixed(col_ndx, RLMDateTimeForNSDate(date));
|
||||
return;
|
||||
}
|
||||
if (NSData *data = RLMDynamicCast<NSData>(val)) {
|
||||
obj->_row.set_mixed(col_ndx, RLMBinaryDataForNSData(data));
|
||||
return;
|
||||
}
|
||||
if (NSNumber *number = RLMDynamicCast<NSNumber>(val)) {
|
||||
switch (number.objCType[0]) {
|
||||
case 'i':
|
||||
case 's':
|
||||
case 'l':
|
||||
case 'q':
|
||||
obj->_row.set_mixed(col_ndx, number.longLongValue);
|
||||
return;
|
||||
case 'f':
|
||||
obj->_row.set_mixed(col_ndx, number.floatValue);
|
||||
return;
|
||||
case 'd':
|
||||
obj->_row.set_mixed(col_ndx, number.doubleValue);
|
||||
return;
|
||||
case 'B':
|
||||
case 'c':
|
||||
obj->_row.set_mixed(col_ndx, (bool)number.boolValue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@throw RLMException(@"Inserting invalid object of class %@ for an RLMPropertyTypeAny property (%@).", [val class], [obj->_objectSchema.properties[col_ndx] name]);
|
||||
}
|
||||
|
||||
// dynamic getter with column closure
|
||||
static IMP RLMAccessorGetter(RLMProperty *prop, RLMAccessorCode accessorCode) {
|
||||
NSUInteger colIndex = prop.column;
|
||||
NSString *name = prop.name;
|
||||
NSString *objectClassName = prop.objectClassName;
|
||||
switch (accessorCode) {
|
||||
case RLMAccessorCodeByte:
|
||||
return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) {
|
||||
return (char)RLMGetLong(obj, colIndex);
|
||||
});
|
||||
case RLMAccessorCodeShort:
|
||||
return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) {
|
||||
return (short)RLMGetLong(obj, colIndex);
|
||||
});
|
||||
case RLMAccessorCodeInt:
|
||||
return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) {
|
||||
return (int)RLMGetLong(obj, colIndex);
|
||||
});
|
||||
case RLMAccessorCodeLongLong:
|
||||
return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) {
|
||||
return RLMGetLong(obj, colIndex);
|
||||
});
|
||||
case RLMAccessorCodeLong:
|
||||
return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) {
|
||||
return (long)RLMGetLong(obj, colIndex);
|
||||
});
|
||||
case RLMAccessorCodeFloat:
|
||||
return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) {
|
||||
return RLMGetFloat(obj, colIndex);
|
||||
});
|
||||
case RLMAccessorCodeDouble:
|
||||
return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) {
|
||||
return RLMGetDouble(obj, colIndex);
|
||||
});
|
||||
case RLMAccessorCodeBool:
|
||||
return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) {
|
||||
return RLMGetBool(obj, colIndex);
|
||||
});
|
||||
case RLMAccessorCodeString:
|
||||
return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) {
|
||||
return RLMGetString(obj, colIndex);
|
||||
});
|
||||
case RLMAccessorCodeDate:
|
||||
return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) {
|
||||
return RLMGetDate(obj, colIndex);
|
||||
});
|
||||
case RLMAccessorCodeData:
|
||||
return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) {
|
||||
return RLMGetData(obj, colIndex);
|
||||
});
|
||||
case RLMAccessorCodeLink:
|
||||
return imp_implementationWithBlock(^id(__unsafe_unretained RLMObjectBase *const obj) {
|
||||
return RLMGetLink(obj, colIndex, objectClassName);
|
||||
});
|
||||
case RLMAccessorCodeArray:
|
||||
return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) {
|
||||
return RLMGetArray(obj, colIndex, objectClassName, name);
|
||||
});
|
||||
case RLMAccessorCodeAny:
|
||||
return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) {
|
||||
return RLMGetAnyProperty(obj, colIndex);
|
||||
});
|
||||
case RLMAccessorCodeIntObject:
|
||||
return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) {
|
||||
return RLMGetIntObject(obj, colIndex);
|
||||
});
|
||||
case RLMAccessorCodeFloatObject:
|
||||
return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) {
|
||||
return RLMGetFloatObject(obj, colIndex);
|
||||
});
|
||||
case RLMAccessorCodeDoubleObject:
|
||||
return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) {
|
||||
return RLMGetDoubleObject(obj, colIndex);
|
||||
});
|
||||
case RLMAccessorCodeBoolObject:
|
||||
return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) {
|
||||
return RLMGetBoolObject(obj, colIndex);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Function>
|
||||
static void RLMWrapSetter(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained NSString *const name, Function&& f) {
|
||||
if (RLMObservationInfo *info = RLMGetObservationInfo(obj->_observationInfo, obj->_row.get_index(), obj->_objectSchema)) {
|
||||
info->willChange(name);
|
||||
f();
|
||||
info->didChange(name);
|
||||
}
|
||||
else {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ArgType, typename StorageType=ArgType>
|
||||
static IMP RLMMakeSetter(RLMProperty *prop) {
|
||||
NSUInteger colIndex = prop.column;
|
||||
NSString *name = prop.name;
|
||||
if (prop.isPrimary) {
|
||||
return imp_implementationWithBlock(^(__unused RLMObjectBase *obj, __unused ArgType val) {
|
||||
@throw RLMException(@"Primary key can't be changed after an object is inserted.");
|
||||
});
|
||||
}
|
||||
return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj, ArgType val) {
|
||||
RLMWrapSetter(obj, name, [&] {
|
||||
RLMSetValue(obj, colIndex, static_cast<StorageType>(val));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// dynamic setter with column closure
|
||||
static IMP RLMAccessorSetter(RLMProperty *prop, RLMAccessorCode accessorCode) {
|
||||
switch (accessorCode) {
|
||||
case RLMAccessorCodeByte: return RLMMakeSetter<char, long long>(prop);
|
||||
case RLMAccessorCodeShort: return RLMMakeSetter<short, long long>(prop);
|
||||
case RLMAccessorCodeInt: return RLMMakeSetter<int, long long>(prop);
|
||||
case RLMAccessorCodeLong: return RLMMakeSetter<long, long long>(prop);
|
||||
case RLMAccessorCodeLongLong: return RLMMakeSetter<long long>(prop);
|
||||
case RLMAccessorCodeFloat: return RLMMakeSetter<float>(prop);
|
||||
case RLMAccessorCodeDouble: return RLMMakeSetter<double>(prop);
|
||||
case RLMAccessorCodeBool: return RLMMakeSetter<BOOL>(prop);
|
||||
case RLMAccessorCodeString: return RLMMakeSetter<NSString *>(prop);
|
||||
case RLMAccessorCodeDate: return RLMMakeSetter<NSDate *>(prop);
|
||||
case RLMAccessorCodeData: return RLMMakeSetter<NSData *>(prop);
|
||||
case RLMAccessorCodeLink: return RLMMakeSetter<RLMObjectBase *>(prop);
|
||||
case RLMAccessorCodeArray: return RLMMakeSetter<RLMArray *>(prop);
|
||||
case RLMAccessorCodeAny: return RLMMakeSetter<id>(prop);
|
||||
case RLMAccessorCodeIntObject: return RLMMakeSetter<NSNumber<RLMInt> *>(prop);
|
||||
case RLMAccessorCodeFloatObject: return RLMMakeSetter<NSNumber<RLMFloat> *>(prop);
|
||||
case RLMAccessorCodeDoubleObject: return RLMMakeSetter<NSNumber<RLMDouble> *>(prop);
|
||||
case RLMAccessorCodeBoolObject: return RLMMakeSetter<NSNumber<RLMBool> *>(prop);
|
||||
}
|
||||
}
|
||||
|
||||
// call getter for superclass for property at colIndex
|
||||
static id RLMSuperGet(RLMObjectBase *obj, NSString *propName) {
|
||||
typedef id (*getter_type)(RLMObjectBase *, SEL);
|
||||
RLMProperty *prop = obj->_objectSchema[propName];
|
||||
Class superClass = class_getSuperclass(obj.class);
|
||||
getter_type superGetter = (getter_type)[superClass instanceMethodForSelector:prop.getterSel];
|
||||
return superGetter(obj, prop.getterSel);
|
||||
}
|
||||
|
||||
// call setter for superclass for property at colIndex
|
||||
static void RLMSuperSet(RLMObjectBase *obj, NSString *propName, id val) {
|
||||
typedef void (*setter_type)(RLMObjectBase *, SEL, RLMArray *ar);
|
||||
RLMProperty *prop = obj->_objectSchema[propName];
|
||||
Class superClass = class_getSuperclass(obj.class);
|
||||
setter_type superSetter = (setter_type)[superClass instanceMethodForSelector:prop.setterSel];
|
||||
superSetter(obj, prop.setterSel, val);
|
||||
}
|
||||
|
||||
// getter/setter for standalone
|
||||
static IMP RLMAccessorStandaloneGetter(RLMProperty *prop, RLMAccessorCode accessorCode) {
|
||||
// only override getters for RLMArray properties
|
||||
if (accessorCode == RLMAccessorCodeArray) {
|
||||
NSString *objectClassName = prop.objectClassName;
|
||||
NSString *propName = prop.name;
|
||||
|
||||
return imp_implementationWithBlock(^(RLMObjectBase *obj) {
|
||||
id val = RLMSuperGet(obj, propName);
|
||||
if (!val) {
|
||||
val = [[RLMArray alloc] initWithObjectClassName:objectClassName];
|
||||
RLMSuperSet(obj, propName, val);
|
||||
}
|
||||
return val;
|
||||
});
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
static IMP RLMAccessorStandaloneSetter(RLMProperty *prop, RLMAccessorCode accessorCode) {
|
||||
// only override getters for RLMArray properties
|
||||
if (accessorCode == RLMAccessorCodeArray) {
|
||||
NSString *propName = prop.name;
|
||||
NSString *objectClassName = prop.objectClassName;
|
||||
return imp_implementationWithBlock(^(RLMObjectBase *obj, id<NSFastEnumeration> ar) {
|
||||
// make copy when setting (as is the case for all other variants)
|
||||
RLMArray *standaloneAr = [[RLMArray alloc] initWithObjectClassName:objectClassName];
|
||||
[standaloneAr addObjects:ar];
|
||||
RLMSuperSet(obj, propName, standaloneAr);
|
||||
});
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// macros/helpers to generate objc type strings for registering methods
|
||||
#define GETTER_TYPES(C) C "@:"
|
||||
#define SETTER_TYPES(C) "v@:" C
|
||||
|
||||
// getter type strings
|
||||
// NOTE: this typecode is really the the first charachter of the objc/runtime.h type
|
||||
// the @ type maps to multiple core types (string, date, array, mixed, any which are id in objc)
|
||||
static const char *getterTypeStringForObjcCode(char code) {
|
||||
switch (code) {
|
||||
case 's': return GETTER_TYPES("s");
|
||||
case 'i': return GETTER_TYPES("i");
|
||||
case 'l': return GETTER_TYPES("l");
|
||||
case 'q': return GETTER_TYPES("q");
|
||||
case 'f': return GETTER_TYPES("f");
|
||||
case 'd': return GETTER_TYPES("d");
|
||||
case 'B': return GETTER_TYPES("B");
|
||||
case 'c': return GETTER_TYPES("c");
|
||||
case '@': return GETTER_TYPES("@");
|
||||
default: @throw RLMException(@"Invalid accessor code");
|
||||
}
|
||||
}
|
||||
|
||||
// setter type strings
|
||||
// NOTE: this typecode is really the the first charachter of the objc/runtime.h type
|
||||
// the @ type maps to multiple core types (string, date, array, mixed, any which are id in objc)
|
||||
static const char *setterTypeStringForObjcCode(char code) {
|
||||
switch (code) {
|
||||
case 's': return SETTER_TYPES("s");
|
||||
case 'i': return SETTER_TYPES("i");
|
||||
case 'l': return SETTER_TYPES("l");
|
||||
case 'q': return SETTER_TYPES("q");
|
||||
case 'f': return SETTER_TYPES("f");
|
||||
case 'd': return SETTER_TYPES("d");
|
||||
case 'B': return SETTER_TYPES("B");
|
||||
case 'c': return SETTER_TYPES("c");
|
||||
case '@': return SETTER_TYPES("@");
|
||||
default: @throw RLMException(@"Invalid accessor code");
|
||||
}
|
||||
}
|
||||
|
||||
// get accessor lookup code based on objc type and rlm type
|
||||
static RLMAccessorCode accessorCodeForType(char objcTypeCode, RLMPropertyType rlmType) {
|
||||
switch (objcTypeCode) {
|
||||
case 't': return RLMAccessorCodeArray;
|
||||
case '@': // custom accessors for strings and subtables
|
||||
switch (rlmType) { // custom accessor codes for types that map to objc objects
|
||||
case RLMPropertyTypeObject: return RLMAccessorCodeLink;
|
||||
case RLMPropertyTypeString: return RLMAccessorCodeString;
|
||||
case RLMPropertyTypeArray: return RLMAccessorCodeArray;
|
||||
case RLMPropertyTypeDate: return RLMAccessorCodeDate;
|
||||
case RLMPropertyTypeData: return RLMAccessorCodeData;
|
||||
case RLMPropertyTypeAny: return RLMAccessorCodeAny;
|
||||
|
||||
case RLMPropertyTypeBool: return RLMAccessorCodeBoolObject;
|
||||
case RLMPropertyTypeDouble: return RLMAccessorCodeDoubleObject;
|
||||
case RLMPropertyTypeFloat: return RLMAccessorCodeFloatObject;
|
||||
case RLMPropertyTypeInt: return RLMAccessorCodeIntObject;
|
||||
default: break;
|
||||
}
|
||||
case 'c':
|
||||
switch (rlmType) {
|
||||
case RLMPropertyTypeInt: return RLMAccessorCodeByte;
|
||||
case RLMPropertyTypeBool: return RLMAccessorCodeBool;
|
||||
default: break;
|
||||
}
|
||||
case 'B': return RLMAccessorCodeBool;
|
||||
case 's': return RLMAccessorCodeShort;
|
||||
case 'i': return RLMAccessorCodeInt;
|
||||
case 'l': return RLMAccessorCodeLong;
|
||||
case 'q': return RLMAccessorCodeLongLong;
|
||||
case 'f': return RLMAccessorCodeFloat;
|
||||
case 'd': return RLMAccessorCodeDouble;
|
||||
default:
|
||||
@throw RLMException(@"Invalid type for objc typecode");
|
||||
}
|
||||
}
|
||||
|
||||
// implement the class method className on accessors to return the className of the
|
||||
// base object
|
||||
void RLMReplaceClassNameMethod(Class accessorClass, NSString *className) {
|
||||
Class metaClass = object_getClass(accessorClass);
|
||||
IMP imp = imp_implementationWithBlock(^(Class){ return className; });
|
||||
class_addMethod(metaClass, @selector(className), imp, "@@:");
|
||||
}
|
||||
|
||||
// implement the shared schema method
|
||||
void RLMReplaceSharedSchemaMethod(Class accessorClass, RLMObjectSchema *schema) {
|
||||
Class metaClass = object_getClass(accessorClass);
|
||||
IMP imp = imp_implementationWithBlock(^(Class cls) {
|
||||
// This can be called on a subclass of the class that we overrode it on
|
||||
// if that class hasn't been initialized yet
|
||||
if (cls == accessorClass) {
|
||||
return schema;
|
||||
}
|
||||
return [RLMSchema sharedSchemaForClass:cls];
|
||||
});
|
||||
class_addMethod(metaClass, @selector(sharedSchema), imp, "@@:");
|
||||
}
|
||||
|
||||
static NSMutableSet *s_generatedClasses = [NSMutableSet new];
|
||||
static void RLMMarkClassAsGenerated(Class cls) {
|
||||
@synchronized (s_generatedClasses) {
|
||||
[s_generatedClasses addObject:cls];
|
||||
}
|
||||
}
|
||||
|
||||
bool RLMIsGeneratedClass(Class cls) {
|
||||
@synchronized (s_generatedClasses) {
|
||||
return [s_generatedClasses containsObject:cls];
|
||||
}
|
||||
}
|
||||
|
||||
static Class RLMCreateAccessorClass(Class objectClass,
|
||||
RLMObjectSchema *schema,
|
||||
NSString *accessorClassPrefix,
|
||||
IMP (*getterGetter)(RLMProperty *, RLMAccessorCode),
|
||||
IMP (*setterGetter)(RLMProperty *, RLMAccessorCode)) {
|
||||
// throw if no schema, prefix, or object class
|
||||
if (!objectClass || !schema || !accessorClassPrefix) {
|
||||
@throw RLMException(@"Missing arguments");
|
||||
}
|
||||
if (!RLMIsKindOfClass(objectClass, RLMObjectBase.class)) {
|
||||
@throw RLMException(@"objectClass must derive from RLMObject or Object");
|
||||
}
|
||||
|
||||
// create and register proxy class which derives from object class
|
||||
NSString *accessorClassName = [accessorClassPrefix stringByAppendingString:schema.className];
|
||||
Class accClass = objc_getClass(accessorClassName.UTF8String);
|
||||
if (!accClass) {
|
||||
accClass = objc_allocateClassPair(objectClass, accessorClassName.UTF8String, 0);
|
||||
objc_registerClassPair(accClass);
|
||||
}
|
||||
|
||||
// override getters/setters for each propery
|
||||
for (unsigned int propNum = 0; propNum < schema.properties.count; propNum++) {
|
||||
RLMProperty *prop = schema.properties[propNum];
|
||||
RLMAccessorCode accessorCode = accessorCodeForType(prop.objcType, prop.type);
|
||||
if (prop.getterSel && getterGetter) {
|
||||
IMP getterImp = getterGetter(prop, accessorCode);
|
||||
if (getterImp) {
|
||||
class_replaceMethod(accClass, prop.getterSel, getterImp, getterTypeStringForObjcCode(prop.objcType));
|
||||
}
|
||||
}
|
||||
if (prop.setterSel && setterGetter) {
|
||||
IMP setterImp = setterGetter(prop, accessorCode);
|
||||
if (setterImp) {
|
||||
class_replaceMethod(accClass, prop.setterSel, setterImp, setterTypeStringForObjcCode(prop.objcType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RLMMarkClassAsGenerated(accClass);
|
||||
|
||||
return accClass;
|
||||
}
|
||||
|
||||
Class RLMAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema, NSString *prefix) {
|
||||
return RLMCreateAccessorClass(objectClass, schema, prefix, RLMAccessorGetter, RLMAccessorSetter);
|
||||
}
|
||||
|
||||
Class RLMStandaloneAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema) {
|
||||
return RLMCreateAccessorClass(objectClass, schema, @"RLMStandalone_",
|
||||
RLMAccessorStandaloneGetter, RLMAccessorStandaloneSetter);
|
||||
}
|
||||
|
||||
void RLMDynamicValidatedSet(RLMObjectBase *obj, NSString *propName, id val) {
|
||||
RLMObjectSchema *schema = obj->_objectSchema;
|
||||
RLMProperty *prop = schema[propName];
|
||||
if (!prop) {
|
||||
@throw RLMException(@"Invalid property name `%@` for class `%@`.", propName, obj->_objectSchema.className);
|
||||
}
|
||||
if (prop.isPrimary) {
|
||||
@throw RLMException(@"Primary key can't be changed to '%@' after an object is inserted.", val);
|
||||
}
|
||||
if (!RLMIsObjectValidForProperty(val, prop)) {
|
||||
@throw RLMException(@"Invalid property value `%@` for property `%@` of class `%@`", val, propName, obj->_objectSchema.className);
|
||||
}
|
||||
|
||||
RLMDynamicSet(obj, prop, RLMCoerceToNil(val), RLMCreationOptionsPromoteStandalone);
|
||||
}
|
||||
|
||||
void RLMDynamicSet(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained RLMProperty *const prop,
|
||||
__unsafe_unretained id const val, RLMCreationOptions creationOptions) {
|
||||
NSUInteger col = prop.column;
|
||||
RLMWrapSetter(obj, prop.name, [&] {
|
||||
switch (accessorCodeForType(prop.objcType, prop.type)) {
|
||||
case RLMAccessorCodeByte:
|
||||
case RLMAccessorCodeShort:
|
||||
case RLMAccessorCodeInt:
|
||||
case RLMAccessorCodeLong:
|
||||
case RLMAccessorCodeLongLong:
|
||||
if (prop.isPrimary) {
|
||||
RLMSetValueUnique(obj, col, prop.name, [val longLongValue]);
|
||||
}
|
||||
else {
|
||||
RLMSetValue(obj, col, [val longLongValue]);
|
||||
}
|
||||
break;
|
||||
case RLMAccessorCodeFloat:
|
||||
RLMSetValue(obj, col, [val floatValue]);
|
||||
break;
|
||||
case RLMAccessorCodeDouble:
|
||||
RLMSetValue(obj, col, [val doubleValue]);
|
||||
break;
|
||||
case RLMAccessorCodeBool:
|
||||
RLMSetValue(obj, col, [val boolValue]);
|
||||
break;
|
||||
case RLMAccessorCodeIntObject:
|
||||
if (prop.isPrimary) {
|
||||
RLMSetValueUnique(obj, col, prop.name, (NSNumber<RLMInt> *)val);
|
||||
}
|
||||
else {
|
||||
RLMSetValue(obj, col, (NSNumber<RLMInt> *)val);
|
||||
}
|
||||
break;
|
||||
case RLMAccessorCodeFloatObject:
|
||||
RLMSetValue(obj, col, (NSNumber<RLMFloat> *)val);
|
||||
break;
|
||||
case RLMAccessorCodeDoubleObject:
|
||||
RLMSetValue(obj, col, (NSNumber<RLMDouble> *)val);
|
||||
break;
|
||||
case RLMAccessorCodeBoolObject:
|
||||
RLMSetValue(obj, col, (NSNumber<RLMBool> *)val);
|
||||
break;
|
||||
case RLMAccessorCodeString:
|
||||
if (prop.isPrimary) {
|
||||
RLMSetValueUnique(obj, col, prop.name, (NSString *)val);
|
||||
}
|
||||
else {
|
||||
RLMSetValue(obj, col, (NSString *)val);
|
||||
}
|
||||
break;
|
||||
case RLMAccessorCodeDate:
|
||||
RLMSetValue(obj, col, (NSDate *)val);
|
||||
break;
|
||||
case RLMAccessorCodeData:
|
||||
RLMSetValue(obj, col, (NSData *)val);
|
||||
break;
|
||||
case RLMAccessorCodeLink: {
|
||||
if (!val || val == NSNull.null) {
|
||||
RLMSetValue(obj, col, (RLMObjectBase *)nil);
|
||||
}
|
||||
else {
|
||||
RLMSetValue(obj, col, RLMGetLinkedObjectForValue(obj->_realm, prop.objectClassName, val, creationOptions));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RLMAccessorCodeArray:
|
||||
if (!val || val == NSNull.null) {
|
||||
RLMSetValue(obj, col, (id<NSFastEnumeration>)nil);
|
||||
}
|
||||
else {
|
||||
id<NSFastEnumeration> rawLinks = val;
|
||||
NSMutableArray *links = [NSMutableArray array];
|
||||
for (id rawLink in rawLinks) {
|
||||
[links addObject:RLMGetLinkedObjectForValue(obj->_realm, prop.objectClassName, rawLink, creationOptions)];
|
||||
}
|
||||
RLMSetValue(obj, col, links);
|
||||
}
|
||||
break;
|
||||
case RLMAccessorCodeAny:
|
||||
RLMSetValue(obj, col, val);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
RLMProperty *RLMValidatedGetProperty(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained NSString *const propName) {
|
||||
RLMProperty *prop = obj->_objectSchema[propName];
|
||||
if (!prop) {
|
||||
@throw RLMException(@"Invalid property name `%@` for class `%@`.", propName, obj->_objectSchema.className);
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
|
||||
id RLMDynamicGet(__unsafe_unretained RLMObjectBase *obj, __unsafe_unretained RLMProperty *prop) {
|
||||
NSUInteger col = prop.column;
|
||||
switch (accessorCodeForType(prop.objcType, prop.type)) {
|
||||
case RLMAccessorCodeByte: return @((char)RLMGetLong(obj, col));
|
||||
case RLMAccessorCodeShort: return @((short)RLMGetLong(obj, col));
|
||||
case RLMAccessorCodeInt: return @((int)RLMGetLong(obj, col));
|
||||
case RLMAccessorCodeLong: return @((long)RLMGetLong(obj, col));
|
||||
case RLMAccessorCodeLongLong: return @(RLMGetLong(obj, col));
|
||||
case RLMAccessorCodeFloat: return @(RLMGetFloat(obj, col));
|
||||
case RLMAccessorCodeDouble: return @(RLMGetDouble(obj, col));
|
||||
case RLMAccessorCodeBool: return @(RLMGetBool(obj, col));
|
||||
case RLMAccessorCodeString: return RLMGetString(obj, col);
|
||||
case RLMAccessorCodeDate: return RLMGetDate(obj, col);
|
||||
case RLMAccessorCodeData: return RLMGetData(obj, col);
|
||||
case RLMAccessorCodeLink: return RLMGetLink(obj, col, prop.objectClassName);
|
||||
case RLMAccessorCodeArray: return RLMGetArray(obj, col, prop.objectClassName, prop.name);
|
||||
case RLMAccessorCodeAny: return RLMGetAnyProperty(obj, col);
|
||||
case RLMAccessorCodeIntObject: return RLMGetIntObject(obj, col);
|
||||
case RLMAccessorCodeFloatObject: return RLMGetFloatObject(obj, col);
|
||||
case RLMAccessorCodeDoubleObject: return RLMGetDoubleObject(obj, col);
|
||||
case RLMAccessorCodeBoolObject: return RLMGetBoolObject(obj, col);
|
||||
}
|
||||
}
|
||||
240
Example/Pods/Realm/Realm/RLMAnalytics.mm
generated
Normal file
240
Example/Pods/Realm/Realm/RLMAnalytics.mm
generated
Normal file
@@ -0,0 +1,240 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2015 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.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Asynchronously submits build information to Realm if running in an iOS
|
||||
// simulator or on OS X if a debugger is attached. Does nothing if running on an
|
||||
// iOS / watchOS device or if a debugger is *not* attached.
|
||||
//
|
||||
// To be clear: this does *not* run when your app is in production or on
|
||||
// your end-user’s devices; it will only run in the simulator or when a debugger
|
||||
// is attached.
|
||||
//
|
||||
// Why are we doing this? In short, because it helps us build a better product
|
||||
// for you. None of the data personally identifies you, your employer or your
|
||||
// app, but it *will* help us understand what language you use, what iOS
|
||||
// versions you target, etc. Having this info will help prioritizing our time,
|
||||
// adding new features and deprecating old features. Collecting an anonymized
|
||||
// bundle & anonymized MAC is the only way for us to count actual usage of the
|
||||
// other metrics accurately. If we don’t have a way to deduplicate the info
|
||||
// reported, it will be useless, as a single developer building their Swift app
|
||||
// 10 times would report 10 times more than a single Objective-C developer that
|
||||
// only builds once, making the data all but useless.
|
||||
// No one likes sharing data unless it’s necessary, we get it, and we’ve
|
||||
// debated adding this for a long long time. Since Realm is a free product
|
||||
// without an email signup, we feel this is a necessary step so we can collect
|
||||
// relevant data to build a better product for you. If you truly, absolutely
|
||||
// feel compelled to not send this data back to Realm, then you can set an env
|
||||
// variable named REALM_DISABLE_ANALYTICS. Since Realm is free we believe
|
||||
// letting these analytics run is a small price to pay for the product & support
|
||||
// we give you.
|
||||
//
|
||||
// Currently the following information is reported:
|
||||
// - What version of Realm is being used, and from which language (obj-c or Swift).
|
||||
// - What version of OS X it's running on (in case Xcode aggressively drops
|
||||
// support for older versions again, we need to know what we need to support).
|
||||
// - The minimum iOS/OS X version that the application is targeting (again, to
|
||||
// help us decide what versions we need to support).
|
||||
// - An anonymous MAC address and bundle ID to aggregate the other information on.
|
||||
// - What version of Swift is being used (if applicable).
|
||||
|
||||
#import "RLMAnalytics.hpp"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_MAC || (TARGET_OS_WATCH && TARGET_OS_SIMULATOR) || (TARGET_OS_TV && TARGET_OS_SIMULATOR)
|
||||
#import "RLMRealm.h"
|
||||
#import "RLMUtil.hpp"
|
||||
|
||||
#import <array>
|
||||
#import <sys/socket.h>
|
||||
#import <sys/sysctl.h>
|
||||
#import <net/if.h>
|
||||
#import <net/if_dl.h>
|
||||
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
|
||||
#ifndef REALM_COCOA_VERSION
|
||||
#import "RLMVersion.h"
|
||||
#endif
|
||||
|
||||
// Declared for RealmSwiftObjectUtil
|
||||
@interface NSObject (SwiftVersion)
|
||||
+ (NSString *)swiftVersion;
|
||||
@end
|
||||
|
||||
// Wrapper for sysctl() that handles the memory management stuff
|
||||
static auto RLMSysCtl(int *mib, u_int mibSize, size_t *bufferSize) {
|
||||
std::unique_ptr<void, decltype(&free)> buffer(nullptr, &free);
|
||||
|
||||
int ret = sysctl(mib, mibSize, nullptr, bufferSize, nullptr, 0);
|
||||
if (ret != 0) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
buffer.reset(malloc(*bufferSize));
|
||||
if (!buffer) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
ret = sysctl(mib, mibSize, buffer.get(), bufferSize, nullptr, 0);
|
||||
if (ret != 0) {
|
||||
buffer.reset();
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// Get the version of OS X we're running on (even in the simulator this gives
|
||||
// the OS X version and not the simulated iOS version)
|
||||
static NSString *RLMOSVersion() {
|
||||
std::array<int, 2> mib = {CTL_KERN, KERN_OSRELEASE};
|
||||
size_t bufferSize;
|
||||
auto buffer = RLMSysCtl(&mib[0], mib.size(), &bufferSize);
|
||||
if (!buffer) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return [[NSString alloc] initWithBytesNoCopy:buffer.release()
|
||||
length:bufferSize - 1
|
||||
encoding:NSUTF8StringEncoding
|
||||
freeWhenDone:YES];
|
||||
}
|
||||
|
||||
// Hash the data in the given buffer and convert it to a hex-format string
|
||||
static NSString *RLMHashData(const void *bytes, size_t length) {
|
||||
unsigned char buffer[CC_SHA256_DIGEST_LENGTH];
|
||||
CC_SHA256(bytes, static_cast<CC_LONG>(length), buffer);
|
||||
|
||||
char formatted[CC_SHA256_DIGEST_LENGTH * 2 + 1];
|
||||
for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; ++i) {
|
||||
sprintf(formatted + i * 2, "%02x", buffer[i]);
|
||||
}
|
||||
|
||||
return [[NSString alloc] initWithBytes:formatted
|
||||
length:CC_SHA256_DIGEST_LENGTH * 2
|
||||
encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
// Returns the hash of the MAC address of the first network adaptor since the
|
||||
// vendorIdentifier isn't constant between iOS simulators.
|
||||
static NSString *RLMMACAddress() {
|
||||
int en0 = static_cast<int>(if_nametoindex("en0"));
|
||||
if (!en0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
std::array<int, 6> mib = {CTL_NET, PF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, en0};
|
||||
size_t bufferSize;
|
||||
auto buffer = RLMSysCtl(&mib[0], mib.size(), &bufferSize);
|
||||
if (!buffer) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// sockaddr_dl struct is immediately after the if_msghdr struct in the buffer
|
||||
auto sockaddr = reinterpret_cast<sockaddr_dl *>(static_cast<if_msghdr *>(buffer.get()) + 1);
|
||||
auto mac = reinterpret_cast<const unsigned char *>(sockaddr->sdl_data + sockaddr->sdl_nlen);
|
||||
|
||||
return RLMHashData(mac, 6);
|
||||
}
|
||||
|
||||
static NSDictionary *RLMAnalyticsPayload() {
|
||||
NSBundle *appBundle = NSBundle.mainBundle;
|
||||
NSString *hashedBundleID = appBundle.bundleIdentifier;
|
||||
|
||||
// Main bundle isn't always the one of interest (e.g. when running tests
|
||||
// it's xctest rather than the app's bundle), so look for one with a bundle ID
|
||||
if (!hashedBundleID) {
|
||||
for (NSBundle *bundle in NSBundle.allBundles) {
|
||||
if ((hashedBundleID = bundle.bundleIdentifier)) {
|
||||
appBundle = bundle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we found a bundle ID anywhere, hash it as it could contain sensitive
|
||||
// information (e.g. the name of an unnanounced product)
|
||||
if (hashedBundleID) {
|
||||
NSData *data = [hashedBundleID dataUsingEncoding:NSUTF8StringEncoding];
|
||||
hashedBundleID = RLMHashData(data.bytes, data.length);
|
||||
}
|
||||
|
||||
NSString *osVersionString = [[NSProcessInfo processInfo] operatingSystemVersionString];
|
||||
Class swiftObjectUtilClass = NSClassFromString(@"RealmSwiftObjectUtil");
|
||||
BOOL isSwift = swiftObjectUtilClass != nil;
|
||||
NSString *swiftVersion = isSwift ? [swiftObjectUtilClass swiftVersion] : @"N/A";
|
||||
|
||||
static NSString *kUnknownString = @"unknown";
|
||||
NSString *hashedMACAddress = RLMMACAddress() ?: kUnknownString;
|
||||
|
||||
return @{
|
||||
@"event": @"Run",
|
||||
@"properties": @{
|
||||
// MixPanel properties
|
||||
@"token": @"ce0fac19508f6c8f20066d345d360fd0",
|
||||
|
||||
// Anonymous identifiers to deduplicate events
|
||||
@"distinct_id": hashedMACAddress,
|
||||
@"Anonymized MAC Address": hashedMACAddress,
|
||||
@"Anonymized Bundle ID": hashedBundleID ?: kUnknownString,
|
||||
|
||||
// Which version of Realm is being used
|
||||
@"Binding": @"cocoa",
|
||||
@"Language": isSwift ? @"swift" : @"objc",
|
||||
@"Realm Version": REALM_COCOA_VERSION,
|
||||
#if TARGET_OS_WATCH
|
||||
@"Target OS Type": @"watchos",
|
||||
#elif TARGET_OS_TV
|
||||
@"Target OS Type": @"tvos",
|
||||
#elif TARGET_OS_IPHONE
|
||||
@"Target OS Type": @"ios",
|
||||
#else
|
||||
@"Target OS Type": @"osx",
|
||||
#endif
|
||||
@"Swift Version": swiftVersion,
|
||||
// Current OS version the app is targetting
|
||||
@"Target OS Version": osVersionString,
|
||||
// Minimum OS version the app is targetting
|
||||
@"Target OS Minimum Version": appBundle.infoDictionary[@"MinimumOSVersion"] ?: kUnknownString,
|
||||
|
||||
// Host OS version being built on
|
||||
@"Host OS Type": @"osx",
|
||||
@"Host OS Version": RLMOSVersion() ?: kUnknownString,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void RLMSendAnalytics() {
|
||||
if (getenv("REALM_DISABLE_ANALYTICS") || !RLMIsDebuggerAttached() || RLMIsRunningInPlayground()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
NSData *payload = [NSJSONSerialization dataWithJSONObject:RLMAnalyticsPayload() options:0 error:nil];
|
||||
NSString *url = [NSString stringWithFormat:@"https://api.mixpanel.com/track/?data=%@&ip=1", [payload base64EncodedStringWithOptions:0]];
|
||||
|
||||
// No error handling or anything because logging errors annoyed people for no
|
||||
// real benefit, and it's not clear what else we could do
|
||||
[[NSURLSession.sharedSession dataTaskWithURL:[NSURL URLWithString:url]] resume];
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void RLMSendAnalytics() {}
|
||||
|
||||
#endif
|
||||
470
Example/Pods/Realm/Realm/RLMArray.mm
generated
Normal file
470
Example/Pods/Realm/Realm/RLMArray.mm
generated
Normal file
@@ -0,0 +1,470 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RLMArray_Private.hpp"
|
||||
|
||||
#import "RLMObject_Private.h"
|
||||
#import "RLMObjectStore.h"
|
||||
#import "RLMObjectSchema.h"
|
||||
#import "RLMQueryUtil.hpp"
|
||||
#import "RLMSwiftSupport.h"
|
||||
#import "RLMUtil.hpp"
|
||||
|
||||
#import <realm/link_view.hpp>
|
||||
|
||||
// See -countByEnumeratingWithState:objects:count
|
||||
@interface RLMArrayHolder : NSObject {
|
||||
@public
|
||||
std::unique_ptr<id[]> items;
|
||||
}
|
||||
@end
|
||||
@implementation RLMArrayHolder
|
||||
@end
|
||||
|
||||
@implementation RLMArray {
|
||||
@public
|
||||
// array for standalone
|
||||
NSMutableArray *_backingArray;
|
||||
}
|
||||
|
||||
template<typename IndexSetFactory>
|
||||
static void changeArray(__unsafe_unretained RLMArray *const ar, NSKeyValueChange kind, dispatch_block_t f, IndexSetFactory&& is) {
|
||||
if (!ar->_backingArray) {
|
||||
ar->_backingArray = [NSMutableArray new];
|
||||
}
|
||||
|
||||
if (RLMObjectBase *parent = ar->_parentObject) {
|
||||
NSIndexSet *indexes = is();
|
||||
[parent willChange:kind valuesAtIndexes:indexes forKey:ar->_key];
|
||||
f();
|
||||
[parent didChange:kind valuesAtIndexes:indexes forKey:ar->_key];
|
||||
}
|
||||
else {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
static void changeArray(__unsafe_unretained RLMArray *const ar, NSKeyValueChange kind, NSUInteger index, dispatch_block_t f) {
|
||||
changeArray(ar, kind, f, [=] { return [NSIndexSet indexSetWithIndex:index]; });
|
||||
}
|
||||
|
||||
static void changeArray(__unsafe_unretained RLMArray *const ar, NSKeyValueChange kind, NSRange range, dispatch_block_t f) {
|
||||
changeArray(ar, kind, f, [=] { return [NSIndexSet indexSetWithIndexesInRange:range]; });
|
||||
}
|
||||
|
||||
static void changeArray(__unsafe_unretained RLMArray *const ar, NSKeyValueChange kind, NSIndexSet *is, dispatch_block_t f) {
|
||||
changeArray(ar, kind, f, [=] { return is; });
|
||||
}
|
||||
|
||||
- (instancetype)initWithObjectClassName:(NSString *)objectClassName {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_objectClassName = objectClassName;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (RLMRealm *)realm {
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
// Generic implementations for all RLMArray variants
|
||||
//
|
||||
|
||||
- (id)firstObject {
|
||||
if (self.count) {
|
||||
return [self objectAtIndex:0];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (id)lastObject {
|
||||
NSUInteger count = self.count;
|
||||
if (count) {
|
||||
return [self objectAtIndex:count-1];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)addObjects:(id<NSFastEnumeration>)objects {
|
||||
for (id obj in objects) {
|
||||
[self addObject:obj];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addObject:(RLMObject *)object {
|
||||
[self insertObject:object atIndex:self.count];
|
||||
}
|
||||
|
||||
- (void)removeLastObject {
|
||||
NSUInteger count = self.count;
|
||||
if (count) {
|
||||
[self removeObjectAtIndex:count-1];
|
||||
}
|
||||
}
|
||||
|
||||
- (id)objectAtIndexedSubscript:(NSUInteger)index {
|
||||
return [self objectAtIndex:index];
|
||||
}
|
||||
|
||||
- (void)setObject:(id)newValue atIndexedSubscript:(NSUInteger)index {
|
||||
[self replaceObjectAtIndex:index withObject:newValue];
|
||||
}
|
||||
|
||||
//
|
||||
// Standalone RLMArray implementation
|
||||
//
|
||||
|
||||
static void RLMValidateMatchingObjectType(RLMArray *array, RLMObject *object) {
|
||||
if (!object) {
|
||||
@throw RLMException(@"Object must not be nil");
|
||||
}
|
||||
if (![array->_objectClassName isEqualToString:object->_objectSchema.className]) {
|
||||
@throw RLMException(@"Object type '%@' does not match RLMArray type '%@'.", object->_objectSchema.className, array->_objectClassName);
|
||||
}
|
||||
}
|
||||
|
||||
static void RLMValidateArrayBounds(__unsafe_unretained RLMArray *const ar,
|
||||
NSUInteger index, bool allowOnePastEnd=false) {
|
||||
NSUInteger max = ar->_backingArray.count + allowOnePastEnd;
|
||||
if (index >= max) {
|
||||
@throw RLMException(@"Index %llu is out of bounds (must be less than %llu).",
|
||||
(unsigned long long)index, (unsigned long long)max);
|
||||
}
|
||||
}
|
||||
|
||||
- (id)objectAtIndex:(NSUInteger)index {
|
||||
RLMValidateArrayBounds(self, index);
|
||||
if (!_backingArray) {
|
||||
_backingArray = [NSMutableArray new];
|
||||
}
|
||||
return [_backingArray objectAtIndex:index];
|
||||
}
|
||||
|
||||
- (NSUInteger)count {
|
||||
return _backingArray.count;
|
||||
}
|
||||
|
||||
- (BOOL)isInvalidated {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unused __unsafe_unretained id [])buffer count:(__unused NSUInteger)len {
|
||||
if (state->state != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We need to enumerate a copy of the backing array so that it doesn't
|
||||
// reflect changes made during enumeration. This copy has to be autoreleased
|
||||
// (since there's nowhere for us to store a strong reference), and uses
|
||||
// RLMArrayHolder rather than an NSArray because NSArray doesn't guarantee
|
||||
// that it'll use a single contiguous block of memory, and if it doesn't
|
||||
// we'd need to forward multiple calls to this method to the same NSArray,
|
||||
// which would require holding a reference to it somewhere.
|
||||
__autoreleasing RLMArrayHolder *copy = [[RLMArrayHolder alloc] init];
|
||||
copy->items = std::make_unique<id[]>(self.count);
|
||||
|
||||
NSUInteger i = 0;
|
||||
for (id object in _backingArray) {
|
||||
copy->items[i++] = object;
|
||||
}
|
||||
|
||||
state->itemsPtr = (__unsafe_unretained id *)(void *)copy->items.get();
|
||||
// needs to point to something valid, but the whole point of this is so
|
||||
// that it can't be changed
|
||||
state->mutationsPtr = state->extra;
|
||||
state->state = i;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
- (void)addObjectsFromArray:(NSArray *)array {
|
||||
for (id obj in array) {
|
||||
RLMValidateMatchingObjectType(self, obj);
|
||||
}
|
||||
changeArray(self, NSKeyValueChangeInsertion, NSMakeRange(_backingArray.count, array.count), ^{
|
||||
[_backingArray addObjectsFromArray:array];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)insertObject:(RLMObject *)anObject atIndex:(NSUInteger)index {
|
||||
RLMValidateMatchingObjectType(self, anObject);
|
||||
RLMValidateArrayBounds(self, index, true);
|
||||
changeArray(self, NSKeyValueChangeInsertion, index, ^{
|
||||
[_backingArray insertObject:anObject atIndex:index];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)insertObjects:(id<NSFastEnumeration>)objects atIndexes:(NSIndexSet *)indexes {
|
||||
changeArray(self, NSKeyValueChangeInsertion, indexes, ^{
|
||||
NSUInteger currentIndex = [indexes firstIndex];
|
||||
for (RLMObject *obj in objects) {
|
||||
RLMValidateMatchingObjectType(self, obj);
|
||||
[_backingArray insertObject:obj atIndex:currentIndex];
|
||||
currentIndex = [indexes indexGreaterThanIndex:currentIndex];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)removeObjectAtIndex:(NSUInteger)index {
|
||||
RLMValidateArrayBounds(self, index);
|
||||
changeArray(self, NSKeyValueChangeRemoval, index, ^{
|
||||
[_backingArray removeObjectAtIndex:index];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)removeObjectsAtIndexes:(NSIndexSet *)indexes {
|
||||
changeArray(self, NSKeyValueChangeRemoval, indexes, ^{
|
||||
[_backingArray removeObjectsAtIndexes:indexes];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject {
|
||||
RLMValidateMatchingObjectType(self, anObject);
|
||||
RLMValidateArrayBounds(self, index);
|
||||
changeArray(self, NSKeyValueChangeReplacement, index, ^{
|
||||
[_backingArray replaceObjectAtIndex:index withObject:anObject];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)moveObjectAtIndex:(NSUInteger)sourceIndex toIndex:(NSUInteger)destinationIndex {
|
||||
RLMValidateArrayBounds(self, sourceIndex);
|
||||
RLMValidateArrayBounds(self, destinationIndex);
|
||||
RLMObjectBase *original = _backingArray[sourceIndex];
|
||||
|
||||
auto start = std::min(sourceIndex, destinationIndex);
|
||||
auto len = std::max(sourceIndex, destinationIndex) - start + 1;
|
||||
changeArray(self, NSKeyValueChangeReplacement, {start, len}, ^{
|
||||
[_backingArray removeObjectAtIndex:sourceIndex];
|
||||
[_backingArray insertObject:original atIndex:destinationIndex];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)exchangeObjectAtIndex:(NSUInteger)index1 withObjectAtIndex:(NSUInteger)index2 {
|
||||
RLMValidateArrayBounds(self, index1);
|
||||
RLMValidateArrayBounds(self, index2);
|
||||
|
||||
changeArray(self, NSKeyValueChangeReplacement, ^{
|
||||
[_backingArray exchangeObjectAtIndex:index1 withObjectAtIndex:index2];
|
||||
}, [=] {
|
||||
NSMutableIndexSet *set = [[NSMutableIndexSet alloc] initWithIndex:index1];
|
||||
[set addIndex:index2];
|
||||
return set;
|
||||
});
|
||||
}
|
||||
|
||||
- (NSUInteger)indexOfObject:(RLMObject *)object {
|
||||
RLMValidateMatchingObjectType(self, object);
|
||||
NSUInteger index = 0;
|
||||
for (RLMObject *cmp in _backingArray) {
|
||||
if (RLMObjectBaseAreEqual(object, cmp)) {
|
||||
return index;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return NSNotFound;
|
||||
}
|
||||
|
||||
- (void)removeAllObjects {
|
||||
changeArray(self, NSKeyValueChangeRemoval, NSMakeRange(0, _backingArray.count), ^{
|
||||
[_backingArray removeAllObjects];
|
||||
});
|
||||
}
|
||||
|
||||
- (RLMResults *)objectsWhere:(NSString *)predicateFormat, ...
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, predicateFormat);
|
||||
RLMResults *results = [self objectsWhere:predicateFormat args:args];
|
||||
va_end(args);
|
||||
return results;
|
||||
}
|
||||
|
||||
- (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args
|
||||
{
|
||||
return [self objectsWithPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]];
|
||||
}
|
||||
|
||||
- (id)valueForKeyPath:(NSString *)keyPath {
|
||||
if (!_backingArray) {
|
||||
return [super valueForKeyPath:keyPath];
|
||||
}
|
||||
// Although delegating to valueForKeyPath: here would allow to support
|
||||
// nested key paths as well, limiting functionality gives consistency
|
||||
// between standalone and persisted arrays.
|
||||
if ([keyPath characterAtIndex:0] == '@') {
|
||||
NSRange operatorRange = [keyPath rangeOfString:@"." options:NSLiteralSearch];
|
||||
if (operatorRange.location != NSNotFound) {
|
||||
NSString *operatorKeyPath = [keyPath substringFromIndex:operatorRange.location + 1];
|
||||
if ([operatorKeyPath rangeOfString:@"."].location != NSNotFound) {
|
||||
@throw RLMException(@"Nested key paths are not supported yet for KVC collection operators.");
|
||||
}
|
||||
}
|
||||
}
|
||||
return [_backingArray valueForKeyPath:keyPath];
|
||||
}
|
||||
|
||||
- (id)valueForKey:(NSString *)key {
|
||||
if ([key isEqualToString:RLMInvalidatedKey]) {
|
||||
return @NO; // Standalone arrays are never invalidated
|
||||
}
|
||||
if (!_backingArray) {
|
||||
return @[];
|
||||
}
|
||||
return [_backingArray valueForKey:key];
|
||||
}
|
||||
|
||||
- (void)setValue:(id)value forKey:(NSString *)key {
|
||||
[_backingArray setValue:value forKey:key];
|
||||
}
|
||||
|
||||
- (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate {
|
||||
if (!_backingArray) {
|
||||
return NSNotFound;
|
||||
}
|
||||
return [_backingArray indexOfObjectPassingTest:^BOOL(id obj, NSUInteger, BOOL *) {
|
||||
return [predicate evaluateWithObject:obj];
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSArray *)objectsAtIndexes:(NSIndexSet *)indexes {
|
||||
if (!_backingArray) {
|
||||
_backingArray = [NSMutableArray new];
|
||||
}
|
||||
return [_backingArray objectsAtIndexes:indexes];
|
||||
}
|
||||
|
||||
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context {
|
||||
RLMValidateArrayObservationKey(keyPath, self);
|
||||
[super addObserver:observer forKeyPath:keyPath options:options context:context];
|
||||
}
|
||||
|
||||
//
|
||||
// Methods unsupported on standalone RLMArray instances
|
||||
//
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
|
||||
- (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate
|
||||
{
|
||||
@throw RLMException(@"This method can only be called on RLMArray instances retrieved from an RLMRealm");
|
||||
}
|
||||
|
||||
- (RLMResults *)sortedResultsUsingProperty:(NSString *)property ascending:(BOOL)ascending
|
||||
{
|
||||
return [self sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithProperty:property ascending:ascending]]];
|
||||
}
|
||||
|
||||
- (RLMResults *)sortedResultsUsingDescriptors:(NSArray *)properties
|
||||
{
|
||||
@throw RLMException(@"This method can only be called on RLMArray instances retrieved from an RLMRealm");
|
||||
}
|
||||
|
||||
// The compiler complains about the method's argument type not matching due to
|
||||
// it not having the generic type attached, but it doesn't seem to be possible
|
||||
// to actually include the generic type
|
||||
// http://www.openradar.me/radar?id=6135653276319744
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wmismatched-parameter-types"
|
||||
- (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMArray *, NSError *))block {
|
||||
@throw RLMException(@"This method can only be called on RLMArray instances retrieved from an RLMRealm");
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat, ...
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, predicateFormat);
|
||||
NSUInteger index = [self indexOfObjectWhere:predicateFormat args:args];
|
||||
va_end(args);
|
||||
return index;
|
||||
}
|
||||
|
||||
- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat args:(va_list)args
|
||||
{
|
||||
return [self indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:predicateFormat
|
||||
arguments:args]];
|
||||
}
|
||||
|
||||
#pragma mark - Superclass Overrides
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [self descriptionWithMaxDepth:RLMDescriptionMaxDepth];
|
||||
}
|
||||
|
||||
- (NSString *)descriptionWithMaxDepth:(NSUInteger)depth {
|
||||
if (depth == 0) {
|
||||
return @"<Maximum depth exceeded>";
|
||||
}
|
||||
|
||||
const NSUInteger maxObjects = 100;
|
||||
NSMutableString *mString = [NSMutableString stringWithFormat:@"RLMArray <%p> (\n", self];
|
||||
unsigned long index = 0, skipped = 0;
|
||||
for (id obj in self) {
|
||||
NSString *sub;
|
||||
if ([obj respondsToSelector:@selector(descriptionWithMaxDepth:)]) {
|
||||
sub = [obj descriptionWithMaxDepth:depth - 1];
|
||||
}
|
||||
else {
|
||||
sub = [obj description];
|
||||
}
|
||||
|
||||
// Indent child objects
|
||||
NSString *objDescription = [sub stringByReplacingOccurrencesOfString:@"\n" withString:@"\n\t"];
|
||||
[mString appendFormat:@"\t[%lu] %@,\n", index++, objDescription];
|
||||
if (index >= maxObjects) {
|
||||
skipped = self.count - maxObjects;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove last comma and newline characters
|
||||
if(self.count > 0)
|
||||
[mString deleteCharactersInRange:NSMakeRange(mString.length-2, 2)];
|
||||
if (skipped) {
|
||||
[mString appendFormat:@"\n\t... %lu objects skipped.", skipped];
|
||||
}
|
||||
[mString appendFormat:@"\n)"];
|
||||
return [NSString stringWithString:mString];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface RLMSortDescriptor ()
|
||||
@property (nonatomic, strong) NSString *property;
|
||||
@property (nonatomic, assign) BOOL ascending;
|
||||
@end
|
||||
|
||||
@implementation RLMSortDescriptor
|
||||
+ (instancetype)sortDescriptorWithProperty:(NSString *)propertyName ascending:(BOOL)ascending {
|
||||
RLMSortDescriptor *desc = [[RLMSortDescriptor alloc] init];
|
||||
desc->_property = propertyName;
|
||||
desc->_ascending = ascending;
|
||||
return desc;
|
||||
}
|
||||
|
||||
- (instancetype)reversedSortDescriptor {
|
||||
return [self.class sortDescriptorWithProperty:_property ascending:!_ascending];
|
||||
}
|
||||
|
||||
@end
|
||||
487
Example/Pods/Realm/Realm/RLMArrayLinkView.mm
generated
Normal file
487
Example/Pods/Realm/Realm/RLMArrayLinkView.mm
generated
Normal file
@@ -0,0 +1,487 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RLMArray_Private.hpp"
|
||||
|
||||
#import "RLMObjectSchema_Private.hpp"
|
||||
#import "RLMObjectStore.h"
|
||||
#import "RLMObject_Private.hpp"
|
||||
#import "RLMObservation.hpp"
|
||||
#import "RLMProperty_Private.h"
|
||||
#import "RLMQueryUtil.hpp"
|
||||
#import "RLMRealm_Private.hpp"
|
||||
#import "RLMSchema.h"
|
||||
#import "RLMUtil.hpp"
|
||||
|
||||
#import "results.hpp"
|
||||
|
||||
#import <realm/table_view.hpp>
|
||||
#import <objc/runtime.h>
|
||||
|
||||
//
|
||||
// RLMArray implementation
|
||||
//
|
||||
@implementation RLMArrayLinkView {
|
||||
@public
|
||||
realm::LinkViewRef _backingLinkView;
|
||||
RLMRealm *_realm;
|
||||
__unsafe_unretained RLMObjectSchema *_containingObjectSchema;
|
||||
std::unique_ptr<RLMObservationInfo> _observationInfo;
|
||||
}
|
||||
|
||||
+ (RLMArrayLinkView *)arrayWithObjectClassName:(NSString *)objectClassName
|
||||
view:(realm::LinkViewRef)view
|
||||
realm:(RLMRealm *)realm
|
||||
key:(NSString *)key
|
||||
parentSchema:(RLMObjectSchema *)parentSchema {
|
||||
RLMArrayLinkView *ar = [[RLMArrayLinkView alloc] initWithObjectClassName:objectClassName];
|
||||
ar->_backingLinkView = view;
|
||||
ar->_realm = realm;
|
||||
ar->_objectSchema = ar->_realm.schema[objectClassName];
|
||||
ar->_containingObjectSchema = parentSchema;
|
||||
ar->_key = key;
|
||||
return ar;
|
||||
}
|
||||
|
||||
void RLMValidateArrayObservationKey(__unsafe_unretained NSString *const keyPath,
|
||||
__unsafe_unretained RLMArray *const array) {
|
||||
if (![keyPath isEqualToString:RLMInvalidatedKey]) {
|
||||
@throw RLMException(@"[<%@ %p> addObserver:forKeyPath:options:context:] is not supported. Key path: %@",
|
||||
[array class], array, keyPath);
|
||||
}
|
||||
}
|
||||
|
||||
void RLMEnsureArrayObservationInfo(std::unique_ptr<RLMObservationInfo>& info,
|
||||
__unsafe_unretained NSString *const keyPath,
|
||||
__unsafe_unretained RLMArray *const array,
|
||||
__unsafe_unretained id const observed) {
|
||||
RLMValidateArrayObservationKey(keyPath, array);
|
||||
if (!info && array.class == [RLMArrayLinkView class]) {
|
||||
RLMArrayLinkView *lv = static_cast<RLMArrayLinkView *>(array);
|
||||
info = std::make_unique<RLMObservationInfo>(lv->_containingObjectSchema,
|
||||
lv->_backingLinkView->get_origin_row_index(),
|
||||
observed);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// validation helpers
|
||||
//
|
||||
static inline void RLMLinkViewArrayValidateAttached(__unsafe_unretained RLMArrayLinkView *const ar) {
|
||||
if (!ar->_backingLinkView->is_attached()) {
|
||||
@throw RLMException(@"RLMArray is no longer valid");
|
||||
}
|
||||
[ar->_realm verifyThread];
|
||||
}
|
||||
static inline void RLMLinkViewArrayValidateInWriteTransaction(__unsafe_unretained RLMArrayLinkView *const ar) {
|
||||
// first verify attached
|
||||
RLMLinkViewArrayValidateAttached(ar);
|
||||
|
||||
if (!ar->_realm.inWriteTransaction) {
|
||||
@throw RLMException(@"Can't mutate a persisted array outside of a write transaction.");
|
||||
}
|
||||
}
|
||||
static inline void RLMValidateObjectClass(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained NSString *const expected) {
|
||||
if (!obj) {
|
||||
@throw RLMException(@"Cannot add `nil` to RLMArray<%@>", expected);
|
||||
}
|
||||
|
||||
NSString *objectClassName = obj->_objectSchema.className;
|
||||
if (![objectClassName isEqualToString:expected]) {
|
||||
@throw RLMException(@"Cannot add object of type '%@' to RLMArray<%@>", objectClassName, expected);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename IndexSetFactory>
|
||||
static void changeArray(__unsafe_unretained RLMArrayLinkView *const ar, NSKeyValueChange kind, dispatch_block_t f, IndexSetFactory&& is) {
|
||||
RLMObservationInfo *info = RLMGetObservationInfo(ar->_observationInfo.get(),
|
||||
ar->_backingLinkView->get_origin_row_index(),
|
||||
ar->_containingObjectSchema);
|
||||
if (info) {
|
||||
NSIndexSet *indexes = is();
|
||||
info->willChange(ar->_key, kind, indexes);
|
||||
f();
|
||||
info->didChange(ar->_key, kind, indexes);
|
||||
}
|
||||
else {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
static void changeArray(__unsafe_unretained RLMArrayLinkView *const ar, NSKeyValueChange kind, NSUInteger index, dispatch_block_t f) {
|
||||
changeArray(ar, kind, f, [=] { return [NSIndexSet indexSetWithIndex:index]; });
|
||||
}
|
||||
|
||||
static void changeArray(__unsafe_unretained RLMArrayLinkView *const ar, NSKeyValueChange kind, NSRange range, dispatch_block_t f) {
|
||||
changeArray(ar, kind, f, [=] { return [NSIndexSet indexSetWithIndexesInRange:range]; });
|
||||
}
|
||||
|
||||
static void changeArray(__unsafe_unretained RLMArrayLinkView *const ar, NSKeyValueChange kind, NSIndexSet *is, dispatch_block_t f) {
|
||||
changeArray(ar, kind, f, [=] { return is; });
|
||||
}
|
||||
|
||||
//
|
||||
// public method implementations
|
||||
//
|
||||
- (RLMRealm *)realm {
|
||||
return _realm;
|
||||
}
|
||||
|
||||
- (NSUInteger)count {
|
||||
RLMLinkViewArrayValidateAttached(self);
|
||||
return _backingLinkView->size();
|
||||
}
|
||||
|
||||
- (BOOL)isInvalidated {
|
||||
return !_backingLinkView->is_attached();
|
||||
}
|
||||
|
||||
// These two methods take advantage of that LinkViews are interned, so there's
|
||||
// only ever at most one LinkView object per SharedGroup for a given row+col.
|
||||
- (BOOL)isEqual:(id)object {
|
||||
if (RLMArrayLinkView *linkView = RLMDynamicCast<RLMArrayLinkView>(object)) {
|
||||
return linkView->_backingLinkView.get() == _backingLinkView.get();
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSUInteger)hash {
|
||||
return reinterpret_cast<NSUInteger>(_backingLinkView.get());
|
||||
}
|
||||
|
||||
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
|
||||
objects:(__unused __unsafe_unretained id [])buffer
|
||||
count:(NSUInteger)len {
|
||||
__autoreleasing RLMFastEnumerator *enumerator;
|
||||
if (state->state == 0) {
|
||||
RLMLinkViewArrayValidateAttached(self);
|
||||
|
||||
enumerator = [[RLMFastEnumerator alloc] initWithCollection:self objectSchema:_objectSchema];
|
||||
state->extra[0] = (long)enumerator;
|
||||
state->extra[1] = self.count;
|
||||
}
|
||||
else {
|
||||
enumerator = (__bridge id)(void *)state->extra[0];
|
||||
}
|
||||
|
||||
return [enumerator countByEnumeratingWithState:state count:len];
|
||||
}
|
||||
|
||||
static void RLMValidateArrayBounds(__unsafe_unretained RLMArrayLinkView *const ar,
|
||||
NSUInteger index, bool allowOnePastEnd=false) {
|
||||
NSUInteger max = ar->_backingLinkView->size() + allowOnePastEnd;
|
||||
if (index >= max) {
|
||||
@throw RLMException(@"Index %llu is out of bounds (must be less than %llu).",
|
||||
(unsigned long long)index, (unsigned long long)max);
|
||||
}
|
||||
}
|
||||
|
||||
- (id)objectAtIndex:(NSUInteger)index {
|
||||
RLMLinkViewArrayValidateAttached(self);
|
||||
RLMValidateArrayBounds(self, index);
|
||||
return RLMCreateObjectAccessor(_realm, _objectSchema, _backingLinkView->get(index).get_index());
|
||||
}
|
||||
|
||||
static void RLMInsertObject(RLMArrayLinkView *ar, RLMObject *object, NSUInteger index) {
|
||||
RLMLinkViewArrayValidateInWriteTransaction(ar);
|
||||
RLMValidateObjectClass(object, ar.objectClassName);
|
||||
|
||||
if (index == NSUIntegerMax) {
|
||||
index = ar->_backingLinkView->size();
|
||||
}
|
||||
else {
|
||||
RLMValidateArrayBounds(ar, index, true);
|
||||
}
|
||||
|
||||
if (object->_realm != ar.realm) {
|
||||
[ar.realm addObject:object];
|
||||
}
|
||||
else if (object->_realm) {
|
||||
if (!object->_row.is_attached()) {
|
||||
@throw RLMException(@"Object has been deleted or invalidated.");
|
||||
}
|
||||
}
|
||||
|
||||
changeArray(ar, NSKeyValueChangeInsertion, index, ^{
|
||||
ar->_backingLinkView->insert(index, object->_row.get_index());
|
||||
});
|
||||
}
|
||||
|
||||
- (void)addObject:(RLMObject *)object {
|
||||
RLMInsertObject(self, object, NSUIntegerMax);
|
||||
}
|
||||
|
||||
- (void)insertObject:(RLMObject *)object atIndex:(NSUInteger)index {
|
||||
RLMInsertObject(self, object, index);
|
||||
}
|
||||
|
||||
- (void)insertObjects:(id<NSFastEnumeration>)objects atIndexes:(NSIndexSet *)indexes {
|
||||
RLMLinkViewArrayValidateInWriteTransaction(self);
|
||||
|
||||
changeArray(self, NSKeyValueChangeInsertion, indexes, ^{
|
||||
NSUInteger index = [indexes firstIndex];
|
||||
for (RLMObject *obj in objects) {
|
||||
if (index > _backingLinkView->size()) {
|
||||
@throw RLMException(@"Trying to insert object at invalid index");
|
||||
}
|
||||
if (obj->_realm != _realm) {
|
||||
[_realm addObject:obj];
|
||||
}
|
||||
else if (!obj->_row.is_attached()) {
|
||||
@throw RLMException(@"Object has been deleted or invalidated.");
|
||||
}
|
||||
|
||||
_backingLinkView->insert(index, obj->_row.get_index());
|
||||
index = [indexes indexGreaterThanIndex:index];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
- (void)removeObjectAtIndex:(NSUInteger)index {
|
||||
RLMLinkViewArrayValidateInWriteTransaction(self);
|
||||
RLMValidateArrayBounds(self, index);
|
||||
changeArray(self, NSKeyValueChangeRemoval, index, ^{
|
||||
_backingLinkView->remove(index);
|
||||
});
|
||||
}
|
||||
|
||||
- (void)removeObjectsAtIndexes:(NSIndexSet *)indexes {
|
||||
RLMLinkViewArrayValidateInWriteTransaction(self);
|
||||
|
||||
changeArray(self, NSKeyValueChangeRemoval, indexes, ^{
|
||||
for (NSUInteger index = [indexes lastIndex]; index != NSNotFound; index = [indexes indexLessThanIndex:index]) {
|
||||
if (index >= _backingLinkView->size()) {
|
||||
@throw RLMException(@"Trying to remove object at invalid index");
|
||||
}
|
||||
_backingLinkView->remove(index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)addObjectsFromArray:(NSArray *)array {
|
||||
RLMLinkViewArrayValidateInWriteTransaction(self);
|
||||
|
||||
changeArray(self, NSKeyValueChangeInsertion, NSMakeRange(_backingLinkView->size(), array.count), ^{
|
||||
for (RLMObject *obj in array) {
|
||||
RLMValidateObjectClass(obj, _objectClassName);
|
||||
if (obj->_realm != _realm) {
|
||||
[_realm addObject:obj];
|
||||
}
|
||||
|
||||
_backingLinkView->add(obj->_row.get_index());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)removeAllObjects {
|
||||
RLMLinkViewArrayValidateInWriteTransaction(self);
|
||||
|
||||
changeArray(self, NSKeyValueChangeRemoval, NSMakeRange(0, _backingLinkView->size()), ^{
|
||||
_backingLinkView->clear();
|
||||
});
|
||||
}
|
||||
|
||||
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(RLMObject *)object {
|
||||
RLMLinkViewArrayValidateInWriteTransaction(self);
|
||||
RLMValidateObjectClass(object, self.objectClassName);
|
||||
RLMValidateArrayBounds(self, index);
|
||||
|
||||
if (object->_realm != self.realm) {
|
||||
[self.realm addObject:object];
|
||||
}
|
||||
|
||||
changeArray(self, NSKeyValueChangeReplacement, index, ^{
|
||||
_backingLinkView->set(index, object->_row.get_index());
|
||||
});
|
||||
}
|
||||
|
||||
- (void)moveObjectAtIndex:(NSUInteger)sourceIndex toIndex:(NSUInteger)destinationIndex {
|
||||
RLMLinkViewArrayValidateInWriteTransaction(self);
|
||||
RLMValidateArrayBounds(self, sourceIndex);
|
||||
RLMValidateArrayBounds(self, destinationIndex);
|
||||
|
||||
auto start = std::min(sourceIndex, destinationIndex);
|
||||
auto len = std::max(sourceIndex, destinationIndex) - start + 1;
|
||||
changeArray(self, NSKeyValueChangeReplacement, {start, len}, ^{
|
||||
_backingLinkView->move(sourceIndex, destinationIndex);
|
||||
});
|
||||
}
|
||||
|
||||
- (void)exchangeObjectAtIndex:(NSUInteger)index1 withObjectAtIndex:(NSUInteger)index2 {
|
||||
RLMLinkViewArrayValidateInWriteTransaction(self);
|
||||
RLMValidateArrayBounds(self, index1);
|
||||
RLMValidateArrayBounds(self, index2);
|
||||
|
||||
changeArray(self, NSKeyValueChangeReplacement, ^{
|
||||
_backingLinkView->swap(index1, index2);
|
||||
}, [=] {
|
||||
NSMutableIndexSet *set = [[NSMutableIndexSet alloc] initWithIndex:index1];
|
||||
[set addIndex:index2];
|
||||
return set;
|
||||
});
|
||||
}
|
||||
|
||||
- (NSUInteger)indexOfObject:(RLMObject *)object {
|
||||
// check attached for table and object
|
||||
RLMLinkViewArrayValidateAttached(self);
|
||||
|
||||
if (object->_realm && !object->_row.is_attached()) {
|
||||
@throw RLMException(@"RLMObject is no longer valid");
|
||||
}
|
||||
|
||||
// check that object types align
|
||||
if (![_objectClassName isEqualToString:object->_objectSchema.className]) {
|
||||
@throw RLMException(@"Object of type (%@) does not match RLMArray type (%@)",
|
||||
object->_objectSchema.className, _objectClassName);
|
||||
}
|
||||
|
||||
// if different tables then no match
|
||||
if (object->_row.get_table() != &_backingLinkView->get_target_table()) {
|
||||
return NSNotFound;
|
||||
}
|
||||
|
||||
// call find on backing array
|
||||
size_t object_ndx = object->_row.get_index();
|
||||
return RLMConvertNotFound(_backingLinkView->find(object_ndx));
|
||||
}
|
||||
|
||||
- (id)valueForKeyPath:(NSString *)keyPath {
|
||||
if ([keyPath hasPrefix:@"@"]) {
|
||||
// Delegate KVC collection operators to RLMResults
|
||||
realm::Query query = _backingLinkView->get_target_table().where(_backingLinkView);
|
||||
RLMResults *results = [RLMResults resultsWithObjectSchema:_realm.schema[self.objectClassName]
|
||||
results:realm::Results(_realm->_realm, std::move(query))];
|
||||
return [results valueForKeyPath:keyPath];
|
||||
}
|
||||
return [super valueForKeyPath:keyPath];
|
||||
}
|
||||
|
||||
- (id)valueForKey:(NSString *)key {
|
||||
// Ideally we'd use "@invalidated" for this so that "invalidated" would use
|
||||
// normal array KVC semantics, but observing @things works very oddly (when
|
||||
// it's part of a key path, it's triggered automatically when array index
|
||||
// changes occur, and can't be sent explicitly, but works normally when it's
|
||||
// the entire key path), and an RLMArrayLinkView *can't* have objects where
|
||||
// invalidated is true, so we're not losing much.
|
||||
if ([key isEqualToString:RLMInvalidatedKey]) {
|
||||
return @(!_backingLinkView->is_attached());
|
||||
}
|
||||
|
||||
RLMLinkViewArrayValidateAttached(self);
|
||||
return RLMCollectionValueForKey(self, key);
|
||||
}
|
||||
|
||||
- (void)setValue:(id)value forKey:(NSString *)key {
|
||||
RLMLinkViewArrayValidateInWriteTransaction(self);
|
||||
RLMCollectionSetValueForKey(self, key, value);
|
||||
}
|
||||
|
||||
- (void)deleteObjectsFromRealm {
|
||||
RLMLinkViewArrayValidateInWriteTransaction(self);
|
||||
|
||||
// delete all target rows from the realm
|
||||
RLMTrackDeletions(_realm, ^{
|
||||
_backingLinkView->remove_all_target_rows();
|
||||
});
|
||||
}
|
||||
|
||||
- (RLMResults *)sortedResultsUsingDescriptors:(NSArray *)properties {
|
||||
RLMLinkViewArrayValidateAttached(self);
|
||||
|
||||
auto results = realm::Results(_realm->_realm,
|
||||
_backingLinkView->get_target_table().where(_backingLinkView),
|
||||
RLMSortOrderFromDescriptors(_realm.schema[_objectClassName], properties));
|
||||
return [RLMResults resultsWithObjectSchema:_realm.schema[self.objectClassName]
|
||||
results:std::move(results)];
|
||||
}
|
||||
|
||||
- (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate {
|
||||
RLMLinkViewArrayValidateAttached(self);
|
||||
|
||||
realm::Query query = _backingLinkView->get_target_table().where(_backingLinkView);
|
||||
RLMUpdateQueryWithPredicate(&query, predicate, _realm.schema, _realm.schema[self.objectClassName]);
|
||||
return [RLMResults resultsWithObjectSchema:_realm.schema[self.objectClassName]
|
||||
results:realm::Results(_realm->_realm, std::move(query))];
|
||||
}
|
||||
|
||||
- (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate {
|
||||
RLMLinkViewArrayValidateAttached(self);
|
||||
|
||||
realm::Query query = _backingLinkView->get_target_table().where(_backingLinkView);
|
||||
RLMUpdateQueryWithPredicate(&query, predicate, _realm.schema, _realm.schema[self.objectClassName]);
|
||||
return RLMConvertNotFound(query.find());
|
||||
}
|
||||
|
||||
- (NSArray *)objectsAtIndexes:(__unused NSIndexSet *)indexes {
|
||||
// FIXME: this is called by KVO when array changes are made. It's not clear
|
||||
// why, and returning nil seems to work fine.
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)addObserver:(id)observer
|
||||
forKeyPath:(NSString *)keyPath
|
||||
options:(NSKeyValueObservingOptions)options
|
||||
context:(void *)context {
|
||||
RLMEnsureArrayObservationInfo(_observationInfo, keyPath, self, self);
|
||||
[super addObserver:observer forKeyPath:keyPath options:options context:context];
|
||||
}
|
||||
|
||||
- (NSUInteger)indexInSource:(NSUInteger)index {
|
||||
return _backingLinkView->get(index).get_index();
|
||||
}
|
||||
|
||||
- (realm::TableView)tableView {
|
||||
return _backingLinkView->get_target_table().where(_backingLinkView).find_all();
|
||||
}
|
||||
|
||||
// The compiler complains about the method's argument type not matching due to
|
||||
// it not having the generic type attached, but it doesn't seem to be possible
|
||||
// to actually include the generic type
|
||||
// http://www.openradar.me/radar?id=6135653276319744
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wmismatched-parameter-types"
|
||||
- (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMArray *, NSError *))block {
|
||||
[_realm verifyNotificationsAreSupported];
|
||||
|
||||
__block uint_fast64_t prevVersion = -1;
|
||||
auto noteBlock = ^(NSString *notification, RLMRealm *) {
|
||||
if (notification != RLMRealmDidChangeNotification) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_backingLinkView->is_attached()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto version = _backingLinkView->get_origin_table().get_version_counter();
|
||||
if (version != prevVersion) {
|
||||
block(self, nil);
|
||||
prevVersion = version;
|
||||
}
|
||||
};
|
||||
|
||||
CFRunLoopPerformBlock(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, ^{
|
||||
noteBlock(RLMRealmDidChangeNotification, nil);
|
||||
});
|
||||
|
||||
return [_realm addNotificationBlock:noteBlock];
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
@end
|
||||
32
Example/Pods/Realm/Realm/RLMConstants.m
generated
Normal file
32
Example/Pods/Realm/Realm/RLMConstants.m
generated
Normal file
@@ -0,0 +1,32 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <Realm/RLMConstants.h>
|
||||
|
||||
NSString * const RLMRealmRefreshRequiredNotification = @"RLMRealmRefreshRequiredNotification";
|
||||
NSString * const RLMRealmDidChangeNotification = @"RLMRealmDidChangeNotification";
|
||||
|
||||
NSString * const RLMErrorDomain = @"io.realm";
|
||||
|
||||
NSString * const RLMExceptionName = @"RLMException";
|
||||
|
||||
NSString * const RLMRealmVersionKey = @"RLMRealmVersion";
|
||||
|
||||
NSString * const RLMRealmCoreVersionKey = @"RLMRealmCoreVersion";
|
||||
|
||||
NSString * const RLMInvalidatedKey = @"invalidated";
|
||||
60
Example/Pods/Realm/Realm/RLMListBase.mm
generated
Normal file
60
Example/Pods/Realm/Realm/RLMListBase.mm
generated
Normal file
@@ -0,0 +1,60 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2015 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 "RLMListBase.h"
|
||||
|
||||
#import "RLMArray_Private.hpp"
|
||||
#import "RLMObservation.hpp"
|
||||
|
||||
@interface RLMArray (KVO)
|
||||
- (NSArray *)objectsAtIndexes:(__unused NSIndexSet *)indexes;
|
||||
@end
|
||||
|
||||
@implementation RLMListBase {
|
||||
std::unique_ptr<RLMObservationInfo> _observationInfo;
|
||||
}
|
||||
|
||||
- (instancetype)initWithArray:(RLMArray *)array {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
__rlmArray = array;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)valueForKey:(NSString *)key {
|
||||
return [__rlmArray valueForKey:key];
|
||||
}
|
||||
|
||||
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len {
|
||||
return [__rlmArray countByEnumeratingWithState:state objects:buffer count:len];
|
||||
}
|
||||
|
||||
- (NSArray *)objectsAtIndexes:(NSIndexSet *)indexes {
|
||||
return [__rlmArray objectsAtIndexes:indexes];
|
||||
}
|
||||
|
||||
- (void)addObserver:(id)observer
|
||||
forKeyPath:(NSString *)keyPath
|
||||
options:(NSKeyValueObservingOptions)options
|
||||
context:(void *)context {
|
||||
RLMEnsureArrayObservationInfo(_observationInfo, keyPath, __rlmArray, self);
|
||||
[super addObserver:observer forKeyPath:keyPath options:options context:context];
|
||||
}
|
||||
|
||||
@end
|
||||
149
Example/Pods/Realm/Realm/RLMMigration.mm
generated
Normal file
149
Example/Pods/Realm/Realm/RLMMigration.mm
generated
Normal file
@@ -0,0 +1,149 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RLMMigration_Private.h"
|
||||
|
||||
#import "RLMAccessor.h"
|
||||
#import "RLMObject.h"
|
||||
#import "RLMObjectSchema_Private.hpp"
|
||||
#import "RLMObjectStore.h"
|
||||
#import "RLMProperty_Private.h"
|
||||
#import "RLMRealm_Dynamic.h"
|
||||
#import "RLMRealm_Private.hpp"
|
||||
#import "RLMResults_Private.h"
|
||||
#import "RLMSchema_Private.h"
|
||||
|
||||
#import "object_store.hpp"
|
||||
#import "shared_realm.hpp"
|
||||
|
||||
using namespace realm;
|
||||
|
||||
// The source realm for a migration has to use a SharedGroup to be able to share
|
||||
// the file with the destination realm, but we don't want to let the user call
|
||||
// beginWriteTransaction on it as that would make no sense.
|
||||
@interface RLMMigrationRealm : RLMRealm
|
||||
@end
|
||||
|
||||
@implementation RLMMigrationRealm
|
||||
- (BOOL)readonly {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)beginWriteTransaction {
|
||||
@throw RLMException(@"Cannot modify the source Realm in a migration");
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation RLMMigration
|
||||
|
||||
- (instancetype)initWithRealm:(RLMRealm *)realm oldRealm:(RLMRealm *)oldRealm {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
// create rw realm to migrate with current on disk table
|
||||
_realm = realm;
|
||||
_oldRealm = oldRealm;
|
||||
object_setClass(_oldRealm, RLMMigrationRealm.class);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (RLMSchema *)oldSchema {
|
||||
return self.oldRealm.schema;
|
||||
}
|
||||
|
||||
- (RLMSchema *)newSchema {
|
||||
return self.realm.schema;
|
||||
}
|
||||
|
||||
- (void)enumerateObjects:(NSString *)className block:(RLMObjectMigrationBlock)block {
|
||||
// get all objects
|
||||
RLMResults *objects = [_realm.schema schemaForClassName:className] ? [_realm allObjects:className] : nil;
|
||||
RLMResults *oldObjects = [_oldRealm.schema schemaForClassName:className] ? [_oldRealm allObjects:className] : nil;
|
||||
|
||||
if (objects && oldObjects) {
|
||||
for (long i = oldObjects.count - 1; i >= 0; i--) {
|
||||
@autoreleasepool {
|
||||
block(oldObjects[i], objects[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (objects) {
|
||||
for (long i = objects.count - 1; i >= 0; i--) {
|
||||
@autoreleasepool {
|
||||
block(nil, objects[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (oldObjects) {
|
||||
for (long i = oldObjects.count - 1; i >= 0; i--) {
|
||||
@autoreleasepool {
|
||||
block(oldObjects[i], nil);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)execute:(RLMMigrationBlock)block {
|
||||
@autoreleasepool {
|
||||
// disable all primary keys for migration
|
||||
for (RLMObjectSchema *objectSchema in _realm.schema.objectSchema) {
|
||||
objectSchema.primaryKeyProperty.isPrimary = NO;
|
||||
}
|
||||
|
||||
// apply block and set new schema version
|
||||
uint64_t oldVersion = _oldRealm->_realm->config().schema_version;
|
||||
block(self, oldVersion);
|
||||
|
||||
_oldRealm = nil;
|
||||
_realm = nil;
|
||||
}
|
||||
}
|
||||
|
||||
-(RLMObject *)createObject:(NSString *)className withValue:(id)value {
|
||||
return [_realm createObject:className withValue:value];
|
||||
}
|
||||
|
||||
- (RLMObject *)createObject:(NSString *)className withObject:(id)object {
|
||||
return [self createObject:className withValue:object];
|
||||
}
|
||||
|
||||
- (void)deleteObject:(RLMObject *)object {
|
||||
[_realm deleteObject:object];
|
||||
}
|
||||
|
||||
- (BOOL)deleteDataForClassName:(NSString *)name {
|
||||
if (!name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TableRef table = ObjectStore::table_for_object_type(_realm.group, name.UTF8String);
|
||||
if (!table) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ([_realm.schema schemaForClassName:name]) {
|
||||
table->clear();
|
||||
}
|
||||
else {
|
||||
realm::ObjectStore::delete_data_for_object(_realm.group, name.UTF8String);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@end
|
||||
217
Example/Pods/Realm/Realm/RLMObject.mm
generated
Normal file
217
Example/Pods/Realm/Realm/RLMObject.mm
generated
Normal file
@@ -0,0 +1,217 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RLMAccessor.h"
|
||||
#import "RLMObject_Private.h"
|
||||
#import "RLMObjectSchema_Private.hpp"
|
||||
#import "RLMObjectStore.h"
|
||||
#import "RLMSchema_Private.h"
|
||||
#import "RLMRealm_Private.hpp"
|
||||
#import "RLMQueryUtil.hpp"
|
||||
|
||||
// We declare things in RLMObject which are actually implemented in RLMObjectBase
|
||||
// for documentation's sake, which leads to -Wunimplemented-method warnings.
|
||||
// Other alternatives to this would be to disable -Wunimplemented-method for this
|
||||
// file (but then we could miss legitimately missing things), or declaring the
|
||||
// inherited things in a category (but they currently aren't nicely grouped for
|
||||
// that).
|
||||
@implementation RLMObject
|
||||
|
||||
// synthesized in RLMObjectBase
|
||||
@dynamic invalidated, realm, objectSchema;
|
||||
|
||||
#pragma mark - Designated Initializers
|
||||
|
||||
- (instancetype)init {
|
||||
return [super init];
|
||||
}
|
||||
|
||||
- (instancetype)initWithValue:(id)value schema:(RLMSchema *)schema {
|
||||
return [super initWithValue:value schema:schema];
|
||||
}
|
||||
|
||||
- (instancetype)initWithRealm:(__unsafe_unretained RLMRealm *const)realm
|
||||
schema:(__unsafe_unretained RLMObjectSchema *const)schema {
|
||||
return [super initWithRealm:realm schema:schema];
|
||||
}
|
||||
|
||||
#pragma mark - Convenience Initializers
|
||||
|
||||
- (instancetype)initWithValue:(id)value {
|
||||
[self.class sharedSchema]; // ensure this class' objectSchema is loaded in the partialSharedSchema
|
||||
RLMSchema *schema = RLMSchema.partialSharedSchema;
|
||||
return [super initWithValue:value schema:schema];
|
||||
}
|
||||
|
||||
- (instancetype)initWithObject:(id)object {
|
||||
return [self initWithValue:object];
|
||||
}
|
||||
|
||||
#pragma mark - Class-based Object Creation
|
||||
|
||||
+ (instancetype)createInDefaultRealmWithValue:(id)value {
|
||||
return (RLMObject *)RLMCreateObjectInRealmWithValue([RLMRealm defaultRealm], [self className], value, false);
|
||||
}
|
||||
|
||||
+ (instancetype)createInDefaultRealmWithObject:(id)object {
|
||||
return [self createInDefaultRealmWithValue:object];
|
||||
}
|
||||
|
||||
+ (instancetype)createInRealm:(RLMRealm *)realm withValue:(id)value {
|
||||
return (RLMObject *)RLMCreateObjectInRealmWithValue(realm, [self className], value, false);
|
||||
}
|
||||
|
||||
+ (instancetype)createInRealm:(RLMRealm *)realm withObject:(id)object {
|
||||
return [self createInRealm:realm withValue:object];
|
||||
}
|
||||
|
||||
+ (instancetype)createOrUpdateInDefaultRealmWithValue:(id)value {
|
||||
return [self createOrUpdateInRealm:[RLMRealm defaultRealm] withValue:value];
|
||||
}
|
||||
|
||||
+ (instancetype)createOrUpdateInDefaultRealmWithObject:(id)object {
|
||||
return [self createOrUpdateInDefaultRealmWithValue:object];
|
||||
}
|
||||
|
||||
+ (instancetype)createOrUpdateInRealm:(RLMRealm *)realm withValue:(id)value {
|
||||
// verify primary key
|
||||
RLMObjectSchema *schema = [self sharedSchema];
|
||||
if (!schema.primaryKeyProperty) {
|
||||
NSString *reason = [NSString stringWithFormat:@"'%@' does not have a primary key and can not be updated", schema.className];
|
||||
@throw [NSException exceptionWithName:@"RLMExecption" reason:reason userInfo:nil];
|
||||
}
|
||||
return (RLMObject *)RLMCreateObjectInRealmWithValue(realm, [self className], value, true);
|
||||
}
|
||||
|
||||
+ (instancetype)createOrUpdateInRealm:(RLMRealm *)realm withObject:(id)object {
|
||||
return [self createOrUpdateInRealm:realm withValue:object];
|
||||
}
|
||||
|
||||
#pragma mark - Subscripting
|
||||
|
||||
- (id)objectForKeyedSubscript:(NSString *)key {
|
||||
return RLMObjectBaseObjectForKeyedSubscript(self, key);
|
||||
}
|
||||
|
||||
- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key {
|
||||
RLMObjectBaseSetObjectForKeyedSubscript(self, key, obj);
|
||||
}
|
||||
|
||||
#pragma mark - Getting & Querying
|
||||
|
||||
+ (RLMResults *)allObjects {
|
||||
return RLMGetObjects(RLMRealm.defaultRealm, self.className, nil);
|
||||
}
|
||||
|
||||
+ (RLMResults *)allObjectsInRealm:(RLMRealm *)realm {
|
||||
return RLMGetObjects(realm, self.className, nil);
|
||||
}
|
||||
|
||||
+ (RLMResults *)objectsWhere:(NSString *)predicateFormat, ... {
|
||||
va_list args;
|
||||
va_start(args, predicateFormat);
|
||||
RLMResults *results = [self objectsWhere:predicateFormat args:args];
|
||||
va_end(args);
|
||||
return results;
|
||||
}
|
||||
|
||||
+ (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args {
|
||||
return [self objectsWithPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]];
|
||||
}
|
||||
|
||||
+ (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat, ... {
|
||||
va_list args;
|
||||
va_start(args, predicateFormat);
|
||||
RLMResults *results = [self objectsInRealm:realm where:predicateFormat args:args];
|
||||
va_end(args);
|
||||
return results;
|
||||
}
|
||||
|
||||
+ (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat args:(va_list)args {
|
||||
return [self objectsInRealm:realm withPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]];
|
||||
}
|
||||
|
||||
+ (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate {
|
||||
return RLMGetObjects(RLMRealm.defaultRealm, self.className, predicate);
|
||||
}
|
||||
|
||||
+ (RLMResults *)objectsInRealm:(RLMRealm *)realm withPredicate:(NSPredicate *)predicate {
|
||||
return RLMGetObjects(realm, self.className, predicate);
|
||||
}
|
||||
|
||||
+ (instancetype)objectForPrimaryKey:(id)primaryKey {
|
||||
return RLMGetObject(RLMRealm.defaultRealm, self.className, primaryKey);
|
||||
}
|
||||
|
||||
+ (instancetype)objectInRealm:(RLMRealm *)realm forPrimaryKey:(id)primaryKey {
|
||||
return RLMGetObject(realm, self.className, primaryKey);
|
||||
}
|
||||
|
||||
#pragma mark - Other Instance Methods
|
||||
|
||||
- (NSArray *)linkingObjectsOfClass:(NSString *)className forProperty:(NSString *)property {
|
||||
return RLMObjectBaseLinkingObjectsOfClass(self, className, property);
|
||||
}
|
||||
|
||||
- (BOOL)isEqualToObject:(RLMObject *)object {
|
||||
return [object isKindOfClass:RLMObject.class] && RLMObjectBaseAreEqual(self, object);
|
||||
}
|
||||
|
||||
+ (NSString *)className {
|
||||
return [super className];
|
||||
}
|
||||
|
||||
#pragma mark - Default values for schema definition
|
||||
|
||||
+ (NSArray *)indexedProperties {
|
||||
return @[];
|
||||
}
|
||||
|
||||
+ (NSDictionary *)defaultPropertyValues {
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSString *)primaryKey {
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSArray *)ignoredProperties {
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSArray *)requiredProperties {
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RLMDynamicObject
|
||||
|
||||
+ (BOOL)shouldIncludeInDefaultSchema {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (id)valueForUndefinedKey:(NSString *)key {
|
||||
return RLMDynamicGet(self, RLMValidatedGetProperty(self, key));
|
||||
}
|
||||
|
||||
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
|
||||
RLMDynamicValidatedSet(self, key, value);
|
||||
}
|
||||
|
||||
@end
|
||||
480
Example/Pods/Realm/Realm/RLMObjectBase.mm
generated
Normal file
480
Example/Pods/Realm/Realm/RLMObjectBase.mm
generated
Normal file
@@ -0,0 +1,480 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RLMObject_Private.hpp"
|
||||
|
||||
#import "RLMAccessor.h"
|
||||
#import "RLMArray_Private.hpp"
|
||||
#import "RLMListBase.h"
|
||||
#import "RLMObjectSchema_Private.hpp"
|
||||
#import "RLMObjectStore.h"
|
||||
#import "RLMObservation.hpp"
|
||||
#import "RLMOptionalBase.h"
|
||||
#import "RLMProperty_Private.h"
|
||||
#import "RLMRealm_Private.hpp"
|
||||
#import "RLMSchema_Private.h"
|
||||
#import "RLMSwiftSupport.h"
|
||||
#import "RLMUtil.hpp"
|
||||
|
||||
using namespace realm;
|
||||
|
||||
const NSUInteger RLMDescriptionMaxDepth = 5;
|
||||
|
||||
static bool RLMInitializedObjectSchema(RLMObjectBase *obj) {
|
||||
obj->_objectSchema = [obj.class sharedSchema];
|
||||
if (!obj->_objectSchema) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// set default values
|
||||
if (!obj->_objectSchema.isSwiftClass) {
|
||||
NSDictionary *dict = RLMDefaultValuesForObjectSchema(obj->_objectSchema);
|
||||
for (NSString *key in dict) {
|
||||
[obj setValue:dict[key] forKey:key];
|
||||
}
|
||||
}
|
||||
|
||||
// set standalone accessor class
|
||||
object_setClass(obj, obj->_objectSchema.standaloneClass);
|
||||
return true;
|
||||
}
|
||||
|
||||
@implementation RLMObjectBase
|
||||
// standalone init
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
RLMInitializedObjectSchema(self);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
// This can't be a unique_ptr because associated objects are removed
|
||||
// *after* c++ members are destroyed, and we need it to still be alive when
|
||||
// that happens
|
||||
delete _observationInfo;
|
||||
}
|
||||
|
||||
static id RLMValidatedObjectForProperty(id obj, RLMProperty *prop, RLMSchema *schema) {
|
||||
if (RLMIsObjectValidForProperty(obj, prop)) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
// check for object or array of properties
|
||||
if (prop.type == RLMPropertyTypeObject) {
|
||||
// for object create and try to initialize with obj
|
||||
RLMObjectSchema *objSchema = schema[prop.objectClassName];
|
||||
return [[objSchema.objectClass alloc] initWithValue:obj schema:schema];
|
||||
}
|
||||
else if (prop.type == RLMPropertyTypeArray && [obj conformsToProtocol:@protocol(NSFastEnumeration)]) {
|
||||
// for arrays, create objects for each element and return new array
|
||||
RLMObjectSchema *objSchema = schema[prop.objectClassName];
|
||||
RLMArray *objects = [[RLMArray alloc] initWithObjectClassName:objSchema.className];
|
||||
for (id el in obj) {
|
||||
[objects addObject:[[objSchema.objectClass alloc] initWithValue:el schema:schema]];
|
||||
}
|
||||
return objects;
|
||||
}
|
||||
|
||||
// if not convertible to prop throw
|
||||
@throw RLMException(@"Invalid value '%@' for property '%@'", obj, prop.name);
|
||||
}
|
||||
|
||||
- (instancetype)initWithValue:(id)value schema:(RLMSchema *)schema {
|
||||
if (!(self = [super init])) {
|
||||
return self;
|
||||
}
|
||||
|
||||
if (!RLMInitializedObjectSchema(self)) {
|
||||
// Don't populate fields from the passed-in object if we're called
|
||||
// during schema init
|
||||
return self;
|
||||
}
|
||||
|
||||
NSArray *properties = _objectSchema.properties;
|
||||
if (NSArray *array = RLMDynamicCast<NSArray>(value)) {
|
||||
if (array.count != properties.count) {
|
||||
@throw RLMException(@"Invalid array input. Number of array elements does not match number of properties.");
|
||||
}
|
||||
for (NSUInteger i = 0; i < array.count; i++) {
|
||||
id propertyValue = RLMValidatedObjectForProperty(array[i], properties[i], schema);
|
||||
[self setValue:RLMCoerceToNil(propertyValue) forKeyPath:[properties[i] name]];
|
||||
}
|
||||
}
|
||||
else {
|
||||
// assume our object is an NSDictionary or an object with kvc properties
|
||||
NSDictionary *defaultValues = nil;
|
||||
for (RLMProperty *prop in properties) {
|
||||
id obj = RLMValidatedValueForProperty(value, prop.name, _objectSchema.className);
|
||||
|
||||
// get default for nil object
|
||||
if (!obj) {
|
||||
if (!defaultValues) {
|
||||
defaultValues = RLMDefaultValuesForObjectSchema(_objectSchema);
|
||||
}
|
||||
obj = defaultValues[prop.name];
|
||||
}
|
||||
|
||||
obj = RLMValidatedObjectForProperty(obj, prop, schema);
|
||||
[self setValue:RLMCoerceToNil(obj) forKeyPath:prop.name];
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithRealm:(__unsafe_unretained RLMRealm *const)realm
|
||||
schema:(__unsafe_unretained RLMObjectSchema *const)schema {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_realm = realm;
|
||||
_objectSchema = schema;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)valueForKey:(NSString *)key {
|
||||
if (_observationInfo) {
|
||||
return _observationInfo->valueForKey(key);
|
||||
}
|
||||
return [super valueForKey:key];
|
||||
}
|
||||
|
||||
// Generic Swift properties can't be dynamic, so KVO doesn't work for them by default
|
||||
- (id)valueForUndefinedKey:(NSString *)key {
|
||||
if (Ivar ivar = _objectSchema[key].swiftIvar) {
|
||||
return RLMCoerceToNil(object_getIvar(self, ivar));
|
||||
}
|
||||
return [super valueForUndefinedKey:key];
|
||||
}
|
||||
|
||||
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
|
||||
RLMProperty *property = _objectSchema[key];
|
||||
if (Ivar ivar = property.swiftIvar) {
|
||||
if (property.type == RLMPropertyTypeArray && [value conformsToProtocol:@protocol(NSFastEnumeration)]) {
|
||||
RLMArray *array = [object_getIvar(self, ivar) _rlmArray];
|
||||
[array removeAllObjects];
|
||||
[array addObjects:value];
|
||||
}
|
||||
else if (property.optional) {
|
||||
RLMOptionalBase *optional = object_getIvar(self, ivar);
|
||||
optional.underlyingValue = value;
|
||||
}
|
||||
return;
|
||||
}
|
||||
[super setValue:value forUndefinedKey:key];
|
||||
}
|
||||
|
||||
// overridden at runtime per-class for performance
|
||||
+ (NSString *)className {
|
||||
NSString *className = NSStringFromClass(self);
|
||||
if ([RLMSwiftSupport isSwiftClassName:className]) {
|
||||
className = [RLMSwiftSupport demangleClassName:className];
|
||||
}
|
||||
return className;
|
||||
}
|
||||
|
||||
// overridden at runtime per-class for performance
|
||||
+ (RLMObjectSchema *)sharedSchema {
|
||||
return [RLMSchema sharedSchemaForClass:self.class];
|
||||
}
|
||||
|
||||
+ (Class)objectUtilClass:(BOOL)isSwift {
|
||||
return RLMObjectUtilClass(isSwift);
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
if (self.isInvalidated) {
|
||||
return @"[invalid object]";
|
||||
}
|
||||
|
||||
return [self descriptionWithMaxDepth:RLMDescriptionMaxDepth];
|
||||
}
|
||||
|
||||
- (NSString *)descriptionWithMaxDepth:(NSUInteger)depth {
|
||||
if (depth == 0) {
|
||||
return @"<Maximum depth exceeded>";
|
||||
}
|
||||
|
||||
NSString *baseClassName = _objectSchema.className;
|
||||
NSMutableString *mString = [NSMutableString stringWithFormat:@"%@ {\n", baseClassName];
|
||||
|
||||
for (RLMProperty *property in _objectSchema.properties) {
|
||||
id object = RLMObjectBaseObjectForKeyedSubscript(self, property.name);
|
||||
NSString *sub;
|
||||
if ([object respondsToSelector:@selector(descriptionWithMaxDepth:)]) {
|
||||
sub = [object descriptionWithMaxDepth:depth - 1];
|
||||
}
|
||||
else if (property.type == RLMPropertyTypeData) {
|
||||
static NSUInteger maxPrintedDataLength = 24;
|
||||
NSData *data = object;
|
||||
NSUInteger length = data.length;
|
||||
if (length > maxPrintedDataLength) {
|
||||
data = [NSData dataWithBytes:data.bytes length:maxPrintedDataLength];
|
||||
}
|
||||
NSString *dataDescription = [data description];
|
||||
sub = [NSString stringWithFormat:@"<%@ — %lu total bytes>", [dataDescription substringWithRange:NSMakeRange(1, dataDescription.length - 2)], (unsigned long)length];
|
||||
}
|
||||
else {
|
||||
sub = [object description];
|
||||
}
|
||||
[mString appendFormat:@"\t%@ = %@;\n", property.name, [sub stringByReplacingOccurrencesOfString:@"\n" withString:@"\n\t"]];
|
||||
}
|
||||
[mString appendString:@"}"];
|
||||
|
||||
return [NSString stringWithString:mString];
|
||||
}
|
||||
|
||||
- (RLMRealm *)realm {
|
||||
return _realm;
|
||||
}
|
||||
|
||||
- (RLMObjectSchema *)objectSchema {
|
||||
return _objectSchema;
|
||||
}
|
||||
|
||||
- (BOOL)isInvalidated {
|
||||
// if not standalone and our accessor has been detached, we have been deleted
|
||||
return self.class == _objectSchema.accessorClass && !_row.is_attached();
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
if (RLMObjectBase *other = RLMDynamicCast<RLMObjectBase>(object)) {
|
||||
if (_objectSchema.primaryKeyProperty) {
|
||||
return RLMObjectBaseAreEqual(self, other);
|
||||
}
|
||||
}
|
||||
return [super isEqual:object];
|
||||
}
|
||||
|
||||
- (NSUInteger)hash {
|
||||
if (_objectSchema.primaryKeyProperty) {
|
||||
id primaryProperty = [self valueForKey:_objectSchema.primaryKeyProperty.name];
|
||||
|
||||
// modify the hash of our primary key value to avoid potential (although unlikely) collisions
|
||||
return [primaryProperty hash] ^ 1;
|
||||
}
|
||||
else {
|
||||
return [super hash];
|
||||
}
|
||||
}
|
||||
|
||||
+ (BOOL)shouldIncludeInDefaultSchema {
|
||||
return RLMIsObjectSubclass(self);
|
||||
}
|
||||
|
||||
- (id)mutableArrayValueForKey:(NSString *)key {
|
||||
id obj = [self valueForKey:key];
|
||||
if ([obj isKindOfClass:[RLMArray class]]) {
|
||||
return obj;
|
||||
}
|
||||
return [super mutableArrayValueForKey:key];
|
||||
}
|
||||
|
||||
- (void)addObserver:(id)observer
|
||||
forKeyPath:(NSString *)keyPath
|
||||
options:(NSKeyValueObservingOptions)options
|
||||
context:(void *)context {
|
||||
if (!_observationInfo) {
|
||||
_observationInfo = new RLMObservationInfo(self);
|
||||
}
|
||||
_observationInfo->recordObserver(_row, _objectSchema, keyPath);
|
||||
|
||||
[super addObserver:observer forKeyPath:keyPath options:options context:context];
|
||||
}
|
||||
|
||||
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath {
|
||||
[super removeObserver:observer forKeyPath:keyPath];
|
||||
_observationInfo->removeObserver();
|
||||
}
|
||||
|
||||
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
|
||||
const char *className = class_getName(self);
|
||||
const char accessorClassPrefix[] = "RLMAccessor_";
|
||||
if (!strncmp(className, accessorClassPrefix, sizeof(accessorClassPrefix) - 1)) {
|
||||
if (self.sharedSchema[key]) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
return [super automaticallyNotifiesObserversForKey:key];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
void RLMObjectBaseSetRealm(__unsafe_unretained RLMObjectBase *object, __unsafe_unretained RLMRealm *realm) {
|
||||
if (object) {
|
||||
object->_realm = realm;
|
||||
}
|
||||
}
|
||||
|
||||
RLMRealm *RLMObjectBaseRealm(__unsafe_unretained RLMObjectBase *object) {
|
||||
return object ? object->_realm : nil;
|
||||
}
|
||||
|
||||
void RLMObjectBaseSetObjectSchema(__unsafe_unretained RLMObjectBase *object, __unsafe_unretained RLMObjectSchema *objectSchema) {
|
||||
if (object) {
|
||||
object->_objectSchema = objectSchema;
|
||||
}
|
||||
}
|
||||
|
||||
RLMObjectSchema *RLMObjectBaseObjectSchema(__unsafe_unretained RLMObjectBase *object) {
|
||||
return object ? object->_objectSchema : nil;
|
||||
}
|
||||
|
||||
NSArray *RLMObjectBaseLinkingObjectsOfClass(RLMObjectBase *object, NSString *className, NSString *property) {
|
||||
if (!object) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!object->_realm) {
|
||||
@throw RLMException(@"Linking object only available for objects in a Realm.");
|
||||
}
|
||||
[object->_realm verifyThread];
|
||||
|
||||
if (!object->_row.is_attached()) {
|
||||
@throw RLMException(@"Object has been deleted or invalidated and is no longer valid.");
|
||||
}
|
||||
|
||||
RLMObjectSchema *schema = object->_realm.schema[className];
|
||||
RLMProperty *prop = schema[property];
|
||||
if (!prop) {
|
||||
@throw RLMException(@"Invalid property '%@'", property);
|
||||
}
|
||||
|
||||
if (![prop.objectClassName isEqualToString:object->_objectSchema.className]) {
|
||||
@throw RLMException(@"Property '%@' of '%@' expected to be an RLMObject or RLMArray property pointing to type '%@'", property, className, object->_objectSchema.className);
|
||||
}
|
||||
|
||||
Table *table = schema.table;
|
||||
if (!table) {
|
||||
return @[];
|
||||
}
|
||||
|
||||
size_t col = prop.column;
|
||||
NSUInteger count = object->_row.get_backlink_count(*table, col);
|
||||
NSMutableArray *links = [NSMutableArray arrayWithCapacity:count];
|
||||
for (NSUInteger i = 0; i < count; i++) {
|
||||
[links addObject:RLMCreateObjectAccessor(object->_realm, schema, object->_row.get_backlink(*table, col, i))];
|
||||
}
|
||||
return [links copy];
|
||||
}
|
||||
|
||||
id RLMObjectBaseObjectForKeyedSubscript(RLMObjectBase *object, NSString *key) {
|
||||
if (!object) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (object->_realm) {
|
||||
return RLMDynamicGet(object, RLMValidatedGetProperty(object, key));
|
||||
}
|
||||
else {
|
||||
return [object valueForKey:key];
|
||||
}
|
||||
}
|
||||
|
||||
void RLMObjectBaseSetObjectForKeyedSubscript(RLMObjectBase *object, NSString *key, id obj) {
|
||||
if (!object) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (object->_realm) {
|
||||
RLMDynamicValidatedSet(object, key, obj);
|
||||
}
|
||||
else {
|
||||
[object setValue:obj forKey:key];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOOL RLMObjectBaseAreEqual(RLMObjectBase *o1, RLMObjectBase *o2) {
|
||||
// if not the correct types throw
|
||||
if ((o1 && ![o1 isKindOfClass:RLMObjectBase.class]) || (o2 && ![o2 isKindOfClass:RLMObjectBase.class])) {
|
||||
@throw RLMException(@"Can only compare objects of class RLMObjectBase");
|
||||
}
|
||||
// if identical object (or both are nil)
|
||||
if (o1 == o2) {
|
||||
return YES;
|
||||
}
|
||||
// if one is nil
|
||||
if (o1 == nil || o2 == nil) {
|
||||
return NO;
|
||||
}
|
||||
// if not in realm or differing realms
|
||||
if (o1->_realm == nil || o1->_realm != o2->_realm) {
|
||||
return NO;
|
||||
}
|
||||
// if either are detached
|
||||
if (!o1->_row.is_attached() || !o2->_row.is_attached()) {
|
||||
return NO;
|
||||
}
|
||||
// if table and index are the same
|
||||
return o1->_row.get_table() == o2->_row.get_table()
|
||||
&& o1->_row.get_index() == o2->_row.get_index();
|
||||
}
|
||||
|
||||
id RLMValidatedValueForProperty(id object, NSString *key, NSString *className) {
|
||||
@try {
|
||||
return [object valueForKey:key];
|
||||
}
|
||||
@catch (NSException *e) {
|
||||
if ([e.name isEqualToString:NSUndefinedKeyException]) {
|
||||
@throw RLMException(@"Invalid value '%@' to initialize object of type '%@': missing key '%@'",
|
||||
object, className, key);
|
||||
}
|
||||
@throw;
|
||||
}
|
||||
}
|
||||
|
||||
Class RLMObjectUtilClass(BOOL isSwift) {
|
||||
static Class objectUtilObjc = [RLMObjectUtil class];
|
||||
static Class objectUtilSwift = NSClassFromString(@"RealmSwiftObjectUtil");
|
||||
return isSwift && objectUtilSwift ? objectUtilSwift : objectUtilObjc;
|
||||
}
|
||||
|
||||
@implementation RLMObjectUtil
|
||||
|
||||
+ (NSArray *)ignoredPropertiesForClass:(Class)cls {
|
||||
return [cls ignoredProperties];
|
||||
}
|
||||
|
||||
+ (NSArray *)indexedPropertiesForClass:(Class)cls {
|
||||
return [cls indexedProperties];
|
||||
}
|
||||
|
||||
+ (NSArray *)getGenericListPropertyNames:(__unused id)obj {
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (void)initializeListProperty:(__unused RLMObjectBase *)object property:(__unused RLMProperty *)property array:(__unused RLMArray *)array {
|
||||
}
|
||||
|
||||
+ (void)initializeOptionalProperty:(__unused RLMObjectBase *)object property:(__unused RLMProperty *)property {
|
||||
}
|
||||
|
||||
+ (NSDictionary *)getOptionalProperties:(__unused id)obj {
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSArray *)requiredPropertiesForClass:(Class)cls {
|
||||
return [cls requiredProperties];
|
||||
}
|
||||
|
||||
@end
|
||||
405
Example/Pods/Realm/Realm/RLMObjectSchema.mm
generated
Normal file
405
Example/Pods/Realm/Realm/RLMObjectSchema.mm
generated
Normal file
@@ -0,0 +1,405 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RLMObjectSchema_Private.hpp"
|
||||
|
||||
#import "RLMArray.h"
|
||||
#import "RLMListBase.h"
|
||||
#import "RLMObject_Private.h"
|
||||
#import "RLMProperty_Private.h"
|
||||
#import "RLMRealm_Dynamic.h"
|
||||
#import "RLMRealm_Private.hpp"
|
||||
#import "RLMSchema_Private.h"
|
||||
#import "RLMSwiftSupport.h"
|
||||
#import "RLMUtil.hpp"
|
||||
|
||||
#import "object_store.hpp"
|
||||
|
||||
using namespace realm;
|
||||
|
||||
// private properties
|
||||
@interface RLMObjectSchema ()
|
||||
@property (nonatomic, readwrite) NSDictionary RLM_GENERIC(id, RLMProperty *) *propertiesByName;
|
||||
@property (nonatomic, readwrite) NSString *className;
|
||||
@end
|
||||
|
||||
@implementation RLMObjectSchema {
|
||||
// table accessor optimization
|
||||
realm::TableRef _table;
|
||||
NSArray *_propertiesInDeclaredOrder;
|
||||
}
|
||||
|
||||
- (instancetype)initWithClassName:(NSString *)objectClassName objectClass:(Class)objectClass properties:(NSArray *)properties {
|
||||
self = [super init];
|
||||
self.className = objectClassName;
|
||||
self.properties = properties;
|
||||
self.objectClass = objectClass;
|
||||
self.accessorClass = objectClass;
|
||||
self.standaloneClass = objectClass;
|
||||
return self;
|
||||
}
|
||||
|
||||
// return properties by name
|
||||
-(RLMProperty *)objectForKeyedSubscript:(id <NSCopying>)key {
|
||||
return _propertiesByName[key];
|
||||
}
|
||||
|
||||
// create property map when setting property array
|
||||
-(void)setProperties:(NSArray *)properties {
|
||||
_properties = properties;
|
||||
NSMutableDictionary *map = [NSMutableDictionary dictionaryWithCapacity:_properties.count];
|
||||
for (RLMProperty *prop in _properties) {
|
||||
map[prop.name] = prop;
|
||||
if (prop.isPrimary) {
|
||||
self.primaryKeyProperty = prop;
|
||||
}
|
||||
}
|
||||
_propertiesByName = map;
|
||||
_propertiesInDeclaredOrder = nil;
|
||||
}
|
||||
|
||||
- (void)setPrimaryKeyProperty:(RLMProperty *)primaryKeyProperty {
|
||||
_primaryKeyProperty.isPrimary = NO;
|
||||
primaryKeyProperty.isPrimary = YES;
|
||||
_primaryKeyProperty = primaryKeyProperty;
|
||||
}
|
||||
|
||||
+ (instancetype)schemaForObjectClass:(Class)objectClass {
|
||||
RLMObjectSchema *schema = [RLMObjectSchema new];
|
||||
|
||||
// determine classname from objectclass as className method has not yet been updated
|
||||
NSString *className = NSStringFromClass(objectClass);
|
||||
bool isSwift = [RLMSwiftSupport isSwiftClassName:className];
|
||||
if (isSwift) {
|
||||
className = [RLMSwiftSupport demangleClassName:className];
|
||||
}
|
||||
schema.className = className;
|
||||
schema.objectClass = objectClass;
|
||||
schema.accessorClass = RLMDynamicObject.class;
|
||||
schema.isSwiftClass = isSwift;
|
||||
|
||||
// create array of RLMProperties, inserting properties of superclasses first
|
||||
Class cls = objectClass;
|
||||
Class superClass = class_getSuperclass(cls);
|
||||
NSArray *props = @[];
|
||||
while (superClass && superClass != RLMObjectBase.class) {
|
||||
props = [[RLMObjectSchema propertiesForClass:cls isSwift:isSwift] arrayByAddingObjectsFromArray:props];
|
||||
cls = superClass;
|
||||
superClass = class_getSuperclass(superClass);
|
||||
}
|
||||
NSUInteger index = 0;
|
||||
for (RLMProperty *prop in props) {
|
||||
prop.declarationIndex = index++;
|
||||
}
|
||||
schema.properties = props;
|
||||
|
||||
// verify that we didn't add any properties twice due to inheritance
|
||||
if (props.count != [NSSet setWithArray:[props valueForKey:@"name"]].count) {
|
||||
NSCountedSet *countedPropertyNames = [NSCountedSet setWithArray:[props valueForKey:@"name"]];
|
||||
NSSet *duplicatePropertyNames = [countedPropertyNames filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary *) {
|
||||
return [countedPropertyNames countForObject:object] > 1;
|
||||
}]];
|
||||
|
||||
if (duplicatePropertyNames.count == 1) {
|
||||
@throw RLMException(@"Property '%@' is declared multiple times in the class hierarchy of '%@'", duplicatePropertyNames.allObjects.firstObject, className);
|
||||
} else {
|
||||
@throw RLMException(@"Object '%@' has properties that are declared multiple times in its class hierarchy: '%@'", className, [duplicatePropertyNames.allObjects componentsJoinedByString:@"', '"]);
|
||||
}
|
||||
}
|
||||
|
||||
if (NSString *primaryKey = [objectClass primaryKey]) {
|
||||
for (RLMProperty *prop in schema.properties) {
|
||||
if ([primaryKey isEqualToString:prop.name]) {
|
||||
prop.indexed = YES;
|
||||
schema.primaryKeyProperty = prop;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!schema.primaryKeyProperty) {
|
||||
@throw RLMException(@"Primary key property '%@' does not exist on object '%@'", primaryKey, className);
|
||||
}
|
||||
if (schema.primaryKeyProperty.type != RLMPropertyTypeInt && schema.primaryKeyProperty.type != RLMPropertyTypeString) {
|
||||
@throw RLMException(@"Only 'string' and 'int' properties can be designated the primary key");
|
||||
}
|
||||
}
|
||||
|
||||
for (RLMProperty *prop in schema.properties) {
|
||||
RLMPropertyType type = prop.type;
|
||||
if (prop.optional && !RLMPropertyTypeIsNullable(type)) {
|
||||
NSString *error = [NSString stringWithFormat:@"Only 'string', 'binary', and 'object' properties can be made optional, and property '%@' is of type '%@'.", prop.name, RLMTypeToString(type)];
|
||||
if (prop.type == RLMPropertyTypeAny && isSwift) {
|
||||
error = [error stringByAppendingString:@"\nIf this is a 'String?' property, it must be declared as 'NSString?' instead."];
|
||||
}
|
||||
@throw RLMException(@"%@", error);
|
||||
}
|
||||
}
|
||||
|
||||
return schema;
|
||||
}
|
||||
|
||||
+ (NSArray *)propertiesForClass:(Class)objectClass isSwift:(bool)isSwiftClass {
|
||||
Class objectUtil = [objectClass objectUtilClass:isSwiftClass];
|
||||
NSArray *ignoredProperties = [objectUtil ignoredPropertiesForClass:objectClass];
|
||||
|
||||
// For Swift classes we need an instance of the object when parsing properties
|
||||
id swiftObjectInstance = isSwiftClass ? [[objectClass alloc] init] : nil;
|
||||
|
||||
unsigned int count;
|
||||
objc_property_t *props = class_copyPropertyList(objectClass, &count);
|
||||
NSMutableArray *propArray = [NSMutableArray arrayWithCapacity:count];
|
||||
NSSet *indexed = [[NSSet alloc] initWithArray:[objectUtil indexedPropertiesForClass:objectClass]];
|
||||
for (unsigned int i = 0; i < count; i++) {
|
||||
NSString *propertyName = @(property_getName(props[i]));
|
||||
if ([ignoredProperties containsObject:propertyName]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RLMProperty *prop = nil;
|
||||
if (isSwiftClass) {
|
||||
prop = [[RLMProperty alloc] initSwiftPropertyWithName:propertyName
|
||||
indexed:[indexed containsObject:propertyName]
|
||||
property:props[i]
|
||||
instance:swiftObjectInstance];
|
||||
}
|
||||
else {
|
||||
prop = [[RLMProperty alloc] initWithName:propertyName indexed:[indexed containsObject:propertyName] property:props[i]];
|
||||
}
|
||||
|
||||
if (prop) {
|
||||
[propArray addObject:prop];
|
||||
}
|
||||
}
|
||||
free(props);
|
||||
|
||||
if (isSwiftClass) {
|
||||
// List<> properties don't show up as objective-C properties due to
|
||||
// being generic, so use Swift reflection to get a list of them, and
|
||||
// then access their ivars directly
|
||||
for (NSString *propName in [objectUtil getGenericListPropertyNames:swiftObjectInstance]) {
|
||||
Ivar ivar = class_getInstanceVariable(objectClass, propName.UTF8String);
|
||||
id value = object_getIvar(swiftObjectInstance, ivar);
|
||||
NSString *className = [value _rlmArray].objectClassName;
|
||||
NSUInteger existing = [propArray indexOfObjectPassingTest:^BOOL(RLMProperty *obj, __unused NSUInteger idx, __unused BOOL *stop) {
|
||||
return [obj.name isEqualToString:propName];
|
||||
}];
|
||||
if (existing != NSNotFound) {
|
||||
[propArray removeObjectAtIndex:existing];
|
||||
}
|
||||
[propArray addObject:[[RLMProperty alloc] initSwiftListPropertyWithName:propName
|
||||
ivar:ivar
|
||||
objectClassName:className]];
|
||||
}
|
||||
}
|
||||
|
||||
if (auto optionalProperties = [objectUtil getOptionalProperties:swiftObjectInstance]) {
|
||||
for (RLMProperty *property in propArray) {
|
||||
property.optional = false;
|
||||
}
|
||||
[optionalProperties enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSNumber *propertyType, __unused BOOL *stop) {
|
||||
if ([ignoredProperties containsObject:propertyName]) {
|
||||
return;
|
||||
}
|
||||
NSUInteger existing = [propArray indexOfObjectPassingTest:^BOOL(RLMProperty *obj, __unused NSUInteger idx, __unused BOOL *stop) {
|
||||
return [obj.name isEqualToString:propertyName];
|
||||
}];
|
||||
RLMProperty *property;
|
||||
if (existing != NSNotFound) {
|
||||
property = propArray[existing];
|
||||
property.optional = true;
|
||||
}
|
||||
if (auto type = RLMCoerceToNil(propertyType)) {
|
||||
if (existing == NSNotFound) {
|
||||
property = [[RLMProperty alloc] initSwiftOptionalPropertyWithName:propertyName
|
||||
indexed:[indexed containsObject:propertyName]
|
||||
ivar:class_getInstanceVariable(objectClass, propertyName.UTF8String)
|
||||
propertyType:RLMPropertyType(type.intValue)];
|
||||
[propArray addObject:property];
|
||||
}
|
||||
else {
|
||||
property.type = RLMPropertyType(type.intValue);
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
if (auto requiredProperties = [objectUtil requiredPropertiesForClass:objectClass]) {
|
||||
for (RLMProperty *property in propArray) {
|
||||
bool required = [requiredProperties containsObject:property.name];
|
||||
if (required && property.type == RLMPropertyTypeObject) {
|
||||
@throw RLMException(@"Object properties cannot be made required, "
|
||||
"but '+[%@ requiredProperties]' included '%@'", objectClass, property.name);
|
||||
}
|
||||
property.optional &= !required;
|
||||
}
|
||||
}
|
||||
|
||||
for (RLMProperty *property in propArray) {
|
||||
if (!property.optional && property.type == RLMPropertyTypeObject) { // remove if/when core supports required link columns
|
||||
@throw RLMException(@"The `%@.%@` property must be marked as being optional.", [objectClass className], property.name);
|
||||
}
|
||||
}
|
||||
|
||||
return propArray;
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone {
|
||||
RLMObjectSchema *schema = [[RLMObjectSchema allocWithZone:zone] init];
|
||||
schema->_objectClass = _objectClass;
|
||||
schema->_className = _className;
|
||||
schema->_objectClass = _objectClass;
|
||||
schema->_accessorClass = _accessorClass;
|
||||
schema->_standaloneClass = _standaloneClass;
|
||||
schema->_isSwiftClass = _isSwiftClass;
|
||||
|
||||
// call property setter to reset map and primary key
|
||||
schema.properties = [[NSArray allocWithZone:zone] initWithArray:_properties copyItems:YES];
|
||||
|
||||
// _table not copied as it's realm::Group-specific
|
||||
return schema;
|
||||
}
|
||||
|
||||
- (instancetype)shallowCopy {
|
||||
RLMObjectSchema *schema = [[RLMObjectSchema alloc] init];
|
||||
schema->_objectClass = _objectClass;
|
||||
schema->_className = _className;
|
||||
schema->_objectClass = _objectClass;
|
||||
schema->_accessorClass = _accessorClass;
|
||||
schema->_standaloneClass = _standaloneClass;
|
||||
schema->_isSwiftClass = _isSwiftClass;
|
||||
|
||||
// reuse propery array, map, and primary key instnaces
|
||||
schema->_properties = _properties;
|
||||
schema->_propertiesByName = _propertiesByName;
|
||||
schema->_primaryKeyProperty = _primaryKeyProperty;
|
||||
|
||||
// _table not copied as it's realm::Group-specific
|
||||
return schema;
|
||||
}
|
||||
|
||||
- (BOOL)isEqualToObjectSchema:(RLMObjectSchema *)objectSchema {
|
||||
if (objectSchema.properties.count != _properties.count) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
// compare ordered list of properties
|
||||
NSArray *otherProperties = objectSchema.properties;
|
||||
for (NSUInteger i = 0; i < _properties.count; i++) {
|
||||
RLMProperty *p1 = _properties[i], *p2 = otherProperties[i];
|
||||
if (p1.type != p2.type ||
|
||||
p1.column != p2.column ||
|
||||
p1.isPrimary != p2.isPrimary ||
|
||||
p1.optional != p2.optional ||
|
||||
![p1.name isEqualToString:p2.name] ||
|
||||
!(p1.objectClassName == p2.objectClassName || [p1.objectClassName isEqualToString:p2.objectClassName])) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
NSMutableString *propertiesString = [NSMutableString string];
|
||||
for (RLMProperty *property in self.properties) {
|
||||
[propertiesString appendFormat:@"\t%@\n", [property.description stringByReplacingOccurrencesOfString:@"\n" withString:@"\n\t"]];
|
||||
}
|
||||
return [NSString stringWithFormat:@"%@ {\n%@}", self.className, propertiesString];
|
||||
}
|
||||
|
||||
- (realm::Table *)table {
|
||||
if (!_table) {
|
||||
_table = ObjectStore::table_for_object_type(_realm.group, _className.UTF8String);
|
||||
}
|
||||
return _table.get();
|
||||
}
|
||||
|
||||
- (void)setTable:(realm::Table *)table {
|
||||
_table.reset(table);
|
||||
}
|
||||
|
||||
- (realm::ObjectSchema)objectStoreCopy {
|
||||
ObjectSchema objectSchema;
|
||||
objectSchema.name = _className.UTF8String;
|
||||
objectSchema.primary_key = _primaryKeyProperty ? _primaryKeyProperty.name.UTF8String : "";
|
||||
for (RLMProperty *prop in _properties) {
|
||||
Property p;
|
||||
p.name = prop.name.UTF8String;
|
||||
p.type = (PropertyType)prop.type;
|
||||
p.object_type = prop.objectClassName ? prop.objectClassName.UTF8String : "";
|
||||
p.is_indexed = prop.indexed;
|
||||
p.is_primary = (prop == _primaryKeyProperty);
|
||||
p.is_nullable = prop.optional;
|
||||
objectSchema.properties.push_back(std::move(p));
|
||||
}
|
||||
return objectSchema;
|
||||
}
|
||||
|
||||
+ (instancetype)objectSchemaForObjectStoreSchema:(realm::ObjectSchema &)objectSchema {
|
||||
RLMObjectSchema *schema = [RLMObjectSchema new];
|
||||
schema.className = @(objectSchema.name.c_str());
|
||||
|
||||
// create array of RLMProperties
|
||||
NSMutableArray *propArray = [NSMutableArray arrayWithCapacity:objectSchema.properties.size()];
|
||||
for (Property &prop : objectSchema.properties) {
|
||||
RLMProperty *property = [[RLMProperty alloc] initWithName:@(prop.name.c_str())
|
||||
type:(RLMPropertyType)prop.type
|
||||
objectClassName:prop.object_type.length() ? @(prop.object_type.c_str()) : nil
|
||||
indexed:prop.is_indexed
|
||||
optional:prop.is_nullable];
|
||||
property.isPrimary = (prop.name == objectSchema.primary_key);
|
||||
[propArray addObject:property];
|
||||
}
|
||||
schema.properties = propArray;
|
||||
|
||||
// get primary key from realm metadata
|
||||
if (objectSchema.primary_key.length()) {
|
||||
NSString *primaryKeyString = [NSString stringWithUTF8String:objectSchema.primary_key.c_str()];
|
||||
schema.primaryKeyProperty = schema[primaryKeyString];
|
||||
if (!schema.primaryKeyProperty) {
|
||||
@throw RLMException(@"No property matching primary key '%@'", primaryKeyString);
|
||||
}
|
||||
}
|
||||
|
||||
// for dynamic schema use vanilla RLMDynamicObject accessor classes
|
||||
schema.objectClass = RLMObject.class;
|
||||
schema.accessorClass = RLMDynamicObject.class;
|
||||
schema.standaloneClass = RLMObject.class;
|
||||
|
||||
return schema;
|
||||
}
|
||||
|
||||
- (void)sortPropertiesByColumn {
|
||||
_properties = [_properties sortedArrayUsingComparator:^NSComparisonResult(RLMProperty *p1, RLMProperty *p2) {
|
||||
if (p1.column < p2.column) return NSOrderedAscending;
|
||||
if (p1.column > p2.column) return NSOrderedDescending;
|
||||
return NSOrderedSame;
|
||||
}];
|
||||
// No need to update the dictionary
|
||||
}
|
||||
|
||||
- (NSArray *)propertiesInDeclaredOrder {
|
||||
if (!_propertiesInDeclaredOrder) {
|
||||
_propertiesInDeclaredOrder = [_properties sortedArrayUsingComparator:^NSComparisonResult(RLMProperty *p1, RLMProperty *p2) {
|
||||
if (p1.declarationIndex < p2.declarationIndex) return NSOrderedAscending;
|
||||
if (p1.declarationIndex > p2.declarationIndex) return NSOrderedDescending;
|
||||
return NSOrderedSame;
|
||||
}];
|
||||
}
|
||||
return _propertiesInDeclaredOrder;
|
||||
}
|
||||
|
||||
@end
|
||||
544
Example/Pods/Realm/Realm/RLMObjectStore.mm
generated
Normal file
544
Example/Pods/Realm/Realm/RLMObjectStore.mm
generated
Normal file
@@ -0,0 +1,544 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RLMObjectStore.h"
|
||||
|
||||
#import "RLMAccessor.h"
|
||||
#import "RLMArray_Private.hpp"
|
||||
#import "RLMListBase.h"
|
||||
#import "RLMObservation.hpp"
|
||||
#import "RLMObject_Private.hpp"
|
||||
#import "RLMObjectSchema_Private.hpp"
|
||||
#import "RLMOptionalBase.h"
|
||||
#import "RLMProperty_Private.h"
|
||||
#import "RLMQueryUtil.hpp"
|
||||
#import "RLMRealm_Private.hpp"
|
||||
#import "RLMSchema_Private.h"
|
||||
#import "RLMSwiftSupport.h"
|
||||
#import "RLMUtil.hpp"
|
||||
|
||||
#import "object_store.hpp"
|
||||
#import "results.hpp"
|
||||
#import "shared_realm.hpp"
|
||||
|
||||
#import <objc/message.h>
|
||||
|
||||
using namespace realm;
|
||||
|
||||
// Schema used to created generated accessors
|
||||
static NSMutableArray * const s_accessorSchema = [NSMutableArray new];
|
||||
|
||||
void RLMRealmCreateAccessors(RLMSchema *schema) {
|
||||
// create accessors for non-dynamic realms
|
||||
RLMSchema *matchingSchema = nil;
|
||||
for (RLMSchema *accessorSchema in s_accessorSchema) {
|
||||
if ([schema isEqualToSchema:accessorSchema]) {
|
||||
matchingSchema = accessorSchema;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matchingSchema) {
|
||||
// reuse accessors
|
||||
for (RLMObjectSchema *objectSchema in schema.objectSchema) {
|
||||
objectSchema.accessorClass = matchingSchema[objectSchema.className].accessorClass;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// create accessors and cache in s_accessorSchema
|
||||
for (RLMObjectSchema *objectSchema in schema.objectSchema) {
|
||||
if (objectSchema.table) {
|
||||
NSString *prefix = [NSString stringWithFormat:@"RLMAccessor_v%lu_",
|
||||
(unsigned long)s_accessorSchema.count];
|
||||
objectSchema.accessorClass = RLMAccessorClassForObjectClass(objectSchema.objectClass, objectSchema, prefix);
|
||||
}
|
||||
}
|
||||
[s_accessorSchema addObject:schema];
|
||||
}
|
||||
}
|
||||
|
||||
void RLMClearAccessorCache() {
|
||||
[s_accessorSchema removeAllObjects];
|
||||
}
|
||||
|
||||
static inline void RLMVerifyRealmRead(__unsafe_unretained RLMRealm *const realm) {
|
||||
if (!realm) {
|
||||
@throw RLMException(@"Realm must not be nil");
|
||||
}
|
||||
[realm verifyThread];
|
||||
}
|
||||
|
||||
static inline void RLMVerifyInWriteTransaction(__unsafe_unretained RLMRealm *const realm) {
|
||||
RLMVerifyRealmRead(realm);
|
||||
// if realm is not writable throw
|
||||
if (!realm.inWriteTransaction) {
|
||||
@throw RLMException(@"Can only add, remove, or create objects in a Realm in a write transaction - call beginWriteTransaction on an RLMRealm instance first.");
|
||||
}
|
||||
}
|
||||
|
||||
void RLMInitializeSwiftAccessorGenerics(__unsafe_unretained RLMObjectBase *const object) {
|
||||
if (!object || !object->_row || !object->_objectSchema.isSwiftClass) {
|
||||
return;
|
||||
}
|
||||
|
||||
static Class s_swiftObjectClass = NSClassFromString(@"RealmSwiftObject");
|
||||
if (![object isKindOfClass:s_swiftObjectClass]) {
|
||||
return; // Is a Swift class using the obj-c API
|
||||
}
|
||||
|
||||
for (RLMProperty *prop in object->_objectSchema.properties) {
|
||||
if (prop.type == RLMPropertyTypeArray) {
|
||||
RLMArray *array = [RLMArrayLinkView arrayWithObjectClassName:prop.objectClassName
|
||||
view:object->_row.get_linklist(prop.column)
|
||||
realm:object->_realm
|
||||
key:prop.name
|
||||
parentSchema:object->_objectSchema];
|
||||
[RLMObjectUtilClass(YES) initializeListProperty:object property:prop array:array];
|
||||
} else if (prop.swiftIvar) {
|
||||
[RLMObjectUtilClass(YES) initializeOptionalProperty:object property:prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
static inline NSUInteger RLMCreateOrGetRowForObject(__unsafe_unretained RLMObjectSchema *const schema, F primaryValueGetter, bool createOrUpdate, bool &created) {
|
||||
// try to get existing row if updating
|
||||
size_t rowIndex = realm::not_found;
|
||||
realm::Table &table = *schema.table;
|
||||
RLMProperty *primaryProperty = schema.primaryKeyProperty;
|
||||
if (createOrUpdate && primaryProperty) {
|
||||
// get primary value
|
||||
id primaryValue = primaryValueGetter(primaryProperty);
|
||||
if (primaryValue == NSNull.null) {
|
||||
primaryValue = nil;
|
||||
}
|
||||
|
||||
// search for existing object based on primary key type
|
||||
if (primaryProperty.type == RLMPropertyTypeString) {
|
||||
rowIndex = table.find_first_string(primaryProperty.column, RLMStringDataWithNSString(primaryValue));
|
||||
}
|
||||
else {
|
||||
rowIndex = table.find_first_int(primaryProperty.column, [primaryValue longLongValue]);
|
||||
}
|
||||
}
|
||||
|
||||
// if no existing, create row
|
||||
created = NO;
|
||||
if (rowIndex == realm::not_found) {
|
||||
rowIndex = table.add_empty_row();
|
||||
created = YES;
|
||||
}
|
||||
|
||||
// get accessor
|
||||
return rowIndex;
|
||||
}
|
||||
|
||||
void RLMAddObjectToRealm(__unsafe_unretained RLMObjectBase *const object,
|
||||
__unsafe_unretained RLMRealm *const realm,
|
||||
bool createOrUpdate) {
|
||||
RLMVerifyInWriteTransaction(realm);
|
||||
|
||||
// verify that object is standalone
|
||||
if (object.invalidated) {
|
||||
@throw RLMException(@"Adding a deleted or invalidated object to a Realm is not permitted");
|
||||
}
|
||||
if (object->_realm) {
|
||||
if (object->_realm == realm) {
|
||||
// no-op
|
||||
return;
|
||||
}
|
||||
// for differing realms users must explicitly create the object in the second realm
|
||||
@throw RLMException(@"Object is already persisted in a Realm");
|
||||
}
|
||||
if (object->_observationInfo && object->_observationInfo->hasObservers()) {
|
||||
@throw RLMException(@"Cannot add an object with observers to a Realm");
|
||||
}
|
||||
|
||||
// set the realm and schema
|
||||
NSString *objectClassName = object->_objectSchema.className;
|
||||
RLMObjectSchema *schema = [realm.schema schemaForClassName:objectClassName];
|
||||
if (!schema) {
|
||||
@throw RLMException(@"Object type '%@' is not persisted in the Realm. "
|
||||
@"If using a custom `objectClasses` / `objectTypes` array in your configuration, "
|
||||
@"add `%@` to the list of `objectClasses` / `objectTypes`.",
|
||||
objectClassName, objectClassName);
|
||||
}
|
||||
object->_objectSchema = schema;
|
||||
object->_realm = realm;
|
||||
|
||||
// get or create row
|
||||
bool created;
|
||||
auto primaryGetter = [=](__unsafe_unretained RLMProperty *const p) { return [object valueForKey:p.getterName]; };
|
||||
object->_row = (*schema.table)[RLMCreateOrGetRowForObject(schema, primaryGetter, createOrUpdate, created)];
|
||||
|
||||
RLMCreationOptions creationOptions = RLMCreationOptionsPromoteStandalone;
|
||||
if (createOrUpdate) {
|
||||
creationOptions |= RLMCreationOptionsCreateOrUpdate;
|
||||
}
|
||||
|
||||
// populate all properties
|
||||
for (RLMProperty *prop in schema.properties) {
|
||||
// get object from ivar using key value coding
|
||||
id value = nil;
|
||||
if (prop.swiftIvar) {
|
||||
if (prop.type == RLMPropertyTypeArray) {
|
||||
value = static_cast<RLMListBase *>(object_getIvar(object, prop.swiftIvar))._rlmArray;
|
||||
}
|
||||
else { // optional
|
||||
value = static_cast<RLMOptionalBase *>(object_getIvar(object, prop.swiftIvar)).underlyingValue;
|
||||
}
|
||||
}
|
||||
else if ([object respondsToSelector:prop.getterSel]) {
|
||||
value = [object valueForKey:prop.getterName];
|
||||
}
|
||||
|
||||
// FIXME: Add condition to check for Mixed once it can support a nil value.
|
||||
if (!value && !prop.optional) {
|
||||
@throw RLMException(@"No value or default value specified for property '%@' in '%@'",
|
||||
prop.name, schema.className);
|
||||
}
|
||||
|
||||
// set in table with out validation
|
||||
// skip primary key when updating since it doesn't change
|
||||
if (created || !prop.isPrimary) {
|
||||
RLMDynamicSet(object, prop, RLMCoerceToNil(value), creationOptions);
|
||||
}
|
||||
|
||||
// set the ivars for object and array properties to nil as otherwise the
|
||||
// accessors retain objects that are no longer accessible via the properties
|
||||
// this is mainly an issue when the object graph being added has cycles,
|
||||
// as it's not obvious that the user has to set the *ivars* to nil to
|
||||
// avoid leaking memory
|
||||
if (prop.type == RLMPropertyTypeObject || prop.type == RLMPropertyTypeArray) {
|
||||
if (!prop.swiftIvar) {
|
||||
((void(*)(id, SEL, id))objc_msgSend)(object, prop.setterSel, nil);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set to proper accessor class
|
||||
object_setClass(object, schema.accessorClass);
|
||||
|
||||
RLMInitializeSwiftAccessorGenerics(object);
|
||||
}
|
||||
|
||||
static void RLMValidateValueForProperty(__unsafe_unretained id const obj,
|
||||
__unsafe_unretained RLMProperty *const prop,
|
||||
__unsafe_unretained RLMSchema *const schema,
|
||||
bool validateNested,
|
||||
bool allowMissing);
|
||||
|
||||
static void RLMValidateValueForObjectSchema(__unsafe_unretained id const value,
|
||||
__unsafe_unretained RLMObjectSchema *const objectSchema,
|
||||
__unsafe_unretained RLMSchema *const schema,
|
||||
bool validateNested,
|
||||
bool allowMissing);
|
||||
|
||||
static void RLMValidateNestedObject(__unsafe_unretained id const obj,
|
||||
__unsafe_unretained NSString *const className,
|
||||
__unsafe_unretained RLMSchema *const schema,
|
||||
bool validateNested,
|
||||
bool allowMissing) {
|
||||
if (obj != nil && obj != NSNull.null) {
|
||||
if (RLMObjectBase *objBase = RLMDynamicCast<RLMObjectBase>(obj)) {
|
||||
RLMObjectSchema *objectSchema = objBase->_objectSchema;
|
||||
if (![className isEqualToString:objectSchema.className]) {
|
||||
// if not the right object class treat as literal
|
||||
RLMValidateValueForObjectSchema(objBase, schema[className], schema, validateNested, allowMissing);
|
||||
}
|
||||
if (objBase.isInvalidated) {
|
||||
@throw RLMException(@"Adding a deleted or invalidated object to a Realm is not permitted");
|
||||
}
|
||||
}
|
||||
else {
|
||||
RLMValidateValueForObjectSchema(obj, schema[className], schema, validateNested, allowMissing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void RLMValidateValueForProperty(__unsafe_unretained id const obj,
|
||||
__unsafe_unretained RLMProperty *const prop,
|
||||
__unsafe_unretained RLMSchema *const schema,
|
||||
bool validateNested,
|
||||
bool allowMissing) {
|
||||
switch (prop.type) {
|
||||
case RLMPropertyTypeString:
|
||||
case RLMPropertyTypeBool:
|
||||
case RLMPropertyTypeDate:
|
||||
case RLMPropertyTypeInt:
|
||||
case RLMPropertyTypeFloat:
|
||||
case RLMPropertyTypeDouble:
|
||||
case RLMPropertyTypeData:
|
||||
case RLMPropertyTypeAny:
|
||||
if (!RLMIsObjectValidForProperty(obj, prop)) {
|
||||
@throw RLMException(@"Invalid value '%@' for property '%@'", obj, prop.name);
|
||||
}
|
||||
break;
|
||||
case RLMPropertyTypeObject:
|
||||
if (validateNested) {
|
||||
RLMValidateNestedObject(obj, prop.objectClassName, schema, validateNested, allowMissing);
|
||||
}
|
||||
break;
|
||||
case RLMPropertyTypeArray: {
|
||||
if (obj != nil && obj != NSNull.null) {
|
||||
if (![obj conformsToProtocol:@protocol(NSFastEnumeration)]) {
|
||||
@throw RLMException(@"Array property value (%@) is not enumerable.", obj);
|
||||
}
|
||||
if (validateNested) {
|
||||
id<NSFastEnumeration> array = obj;
|
||||
for (id el in array) {
|
||||
RLMValidateNestedObject(el, prop.objectClassName, schema, validateNested, allowMissing);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void RLMValidateValueForObjectSchema(__unsafe_unretained id const value,
|
||||
__unsafe_unretained RLMObjectSchema *const objectSchema,
|
||||
__unsafe_unretained RLMSchema *const schema,
|
||||
bool validateNested,
|
||||
bool allowMissing) {
|
||||
NSArray *props = objectSchema.properties;
|
||||
if (NSArray *array = RLMDynamicCast<NSArray>(value)) {
|
||||
if (array.count != props.count) {
|
||||
@throw RLMException(@"Invalid array input. Number of array elements does not match number of properties.");
|
||||
}
|
||||
for (NSUInteger i = 0; i < array.count; i++) {
|
||||
RLMProperty *prop = props[i];
|
||||
RLMValidateValueForProperty(array[i], prop, schema, validateNested, allowMissing);
|
||||
}
|
||||
}
|
||||
else {
|
||||
NSDictionary *defaults;
|
||||
for (RLMProperty *prop in props) {
|
||||
id obj = [value valueForKey:prop.name];
|
||||
|
||||
// get default for nil object
|
||||
if (!obj) {
|
||||
if (!defaults) {
|
||||
defaults = RLMDefaultValuesForObjectSchema(objectSchema);
|
||||
}
|
||||
obj = defaults[prop.name];
|
||||
}
|
||||
if (obj || prop.isPrimary || !allowMissing) {
|
||||
RLMValidateValueForProperty(obj, prop, schema, true, allowMissing);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RLMObjectBase *RLMCreateObjectInRealmWithValue(RLMRealm *realm, NSString *className, id value, bool createOrUpdate = false) {
|
||||
if (createOrUpdate && RLMIsObjectSubclass([value class])) {
|
||||
RLMObjectBase *obj = value;
|
||||
if ([obj->_objectSchema.className isEqualToString:className] && obj->_realm == realm) {
|
||||
// This is a no-op if value is an RLMObject of the same type already backed by the target realm.
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
// verify writable
|
||||
RLMVerifyInWriteTransaction(realm);
|
||||
|
||||
// create the object
|
||||
RLMSchema *schema = realm.schema;
|
||||
RLMObjectSchema *objectSchema = [realm.schema schemaForClassName:className];
|
||||
if (!objectSchema) {
|
||||
@throw RLMException(@"Object type '%@' is not persisted in the Realm. "
|
||||
@"If using a custom `objectClasses` / `objectTypes` array in your configuration, "
|
||||
@"add `%@` to the list of `objectClasses` / `objectTypes`.",
|
||||
className, className);
|
||||
}
|
||||
RLMObjectBase *object = [[objectSchema.accessorClass alloc] initWithRealm:realm schema:objectSchema];
|
||||
|
||||
RLMCreationOptions creationOptions = createOrUpdate ? RLMCreationOptionsCreateOrUpdate : RLMCreationOptionsNone;
|
||||
|
||||
// create row, and populate
|
||||
if (NSArray *array = RLMDynamicCast<NSArray>(value)) {
|
||||
// get or create our accessor
|
||||
bool created;
|
||||
auto primaryGetter = [=](__unsafe_unretained RLMProperty *const p) { return array[p.column]; };
|
||||
object->_row = (*objectSchema.table)[RLMCreateOrGetRowForObject(objectSchema, primaryGetter, createOrUpdate, created)];
|
||||
|
||||
// populate
|
||||
NSArray *props = objectSchema.propertiesInDeclaredOrder;
|
||||
for (NSUInteger i = 0; i < array.count; i++) {
|
||||
RLMProperty *prop = props[i];
|
||||
// skip primary key when updating since it doesn't change
|
||||
if (created || !prop.isPrimary) {
|
||||
id val = array[i];
|
||||
RLMValidateValueForProperty(val, prop, schema, false, false);
|
||||
RLMDynamicSet(object, prop, RLMCoerceToNil(val), creationOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// get or create our accessor
|
||||
bool created;
|
||||
auto primaryGetter = [=](RLMProperty *p) { return [value valueForKey:p.name]; };
|
||||
object->_row = (*objectSchema.table)[RLMCreateOrGetRowForObject(objectSchema, primaryGetter, createOrUpdate, created)];
|
||||
|
||||
// populate
|
||||
NSDictionary *defaultValues = nil;
|
||||
for (RLMProperty *prop in objectSchema.properties) {
|
||||
id propValue = RLMValidatedValueForProperty(value, prop.name, objectSchema.className);
|
||||
|
||||
if (!propValue && created) {
|
||||
if (!defaultValues) {
|
||||
defaultValues = RLMDefaultValuesForObjectSchema(objectSchema);
|
||||
}
|
||||
propValue = defaultValues[prop.name];
|
||||
if (!propValue && (prop.type == RLMPropertyTypeObject || prop.type == RLMPropertyTypeArray)) {
|
||||
propValue = NSNull.null;
|
||||
}
|
||||
}
|
||||
|
||||
if (propValue) {
|
||||
if (created || !prop.isPrimary) {
|
||||
// skip missing properties and primary key when updating since it doesn't change
|
||||
RLMValidateValueForProperty(propValue, prop, schema, false, false);
|
||||
RLMDynamicSet(object, prop, RLMCoerceToNil(propValue), creationOptions);
|
||||
}
|
||||
}
|
||||
else if (created && !prop.optional) {
|
||||
@throw RLMException(@"Property '%@' of object of type '%@' cannot be nil.", prop.name, objectSchema.className);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RLMInitializeSwiftAccessorGenerics(object);
|
||||
return object;
|
||||
}
|
||||
|
||||
void RLMDeleteObjectFromRealm(__unsafe_unretained RLMObjectBase *const object,
|
||||
__unsafe_unretained RLMRealm *const realm) {
|
||||
if (realm != object->_realm) {
|
||||
@throw RLMException(@"Can only delete an object from the Realm it belongs to.");
|
||||
}
|
||||
|
||||
RLMVerifyInWriteTransaction(object->_realm);
|
||||
|
||||
// move last row to row we are deleting
|
||||
if (object->_row.is_attached()) {
|
||||
RLMTrackDeletions(realm, ^{
|
||||
object->_row.get_table()->move_last_over(object->_row.get_index());
|
||||
});
|
||||
}
|
||||
|
||||
// set realm to nil
|
||||
object->_realm = nil;
|
||||
}
|
||||
|
||||
void RLMDeleteAllObjectsFromRealm(RLMRealm *realm) {
|
||||
RLMVerifyInWriteTransaction(realm);
|
||||
|
||||
// clear table for each object schema
|
||||
for (RLMObjectSchema *objectSchema in realm.schema.objectSchema) {
|
||||
RLMClearTable(objectSchema);
|
||||
}
|
||||
}
|
||||
|
||||
RLMResults *RLMGetObjects(RLMRealm *realm, NSString *objectClassName, NSPredicate *predicate) {
|
||||
RLMVerifyRealmRead(realm);
|
||||
|
||||
// create view from table and predicate
|
||||
RLMObjectSchema *objectSchema = realm.schema[objectClassName];
|
||||
if (!objectSchema.table) {
|
||||
// read-only realms may be missing tables since we can't add any
|
||||
// missing ones on init
|
||||
return [RLMResults resultsWithObjectSchema:objectSchema results:{}];
|
||||
}
|
||||
|
||||
if (predicate) {
|
||||
realm::Query query = objectSchema.table->where();
|
||||
RLMUpdateQueryWithPredicate(&query, predicate, realm.schema, objectSchema);
|
||||
|
||||
// create and populate array
|
||||
return [RLMResults resultsWithObjectSchema:objectSchema
|
||||
results:realm::Results(realm->_realm, std::move(query))];
|
||||
}
|
||||
|
||||
return [RLMResults resultsWithObjectSchema:objectSchema
|
||||
results:realm::Results(realm->_realm, *objectSchema.table)];
|
||||
}
|
||||
|
||||
id RLMGetObject(RLMRealm *realm, NSString *objectClassName, id key) {
|
||||
RLMVerifyRealmRead(realm);
|
||||
|
||||
RLMObjectSchema *objectSchema = realm.schema[objectClassName];
|
||||
|
||||
RLMProperty *primaryProperty = objectSchema.primaryKeyProperty;
|
||||
if (!primaryProperty) {
|
||||
@throw RLMException(@"%@ does not have a primary key", objectClassName);
|
||||
}
|
||||
|
||||
if (!objectSchema.table) {
|
||||
// read-only realms may be missing tables since we can't add any
|
||||
// missing ones on init
|
||||
return nil;
|
||||
}
|
||||
|
||||
key = RLMCoerceToNil(key);
|
||||
|
||||
size_t row = realm::not_found;
|
||||
if (primaryProperty.type == RLMPropertyTypeString) {
|
||||
NSString *str = RLMDynamicCast<NSString>(key);
|
||||
if (str || (!key && primaryProperty.optional)) {
|
||||
row = objectSchema.table->find_first_string(primaryProperty.column, RLMStringDataWithNSString(str));
|
||||
}
|
||||
else {
|
||||
@throw RLMException(@"Invalid value '%@' for primary key", key);
|
||||
}
|
||||
}
|
||||
else {
|
||||
NSNumber *number = RLMDynamicCast<NSNumber>(key);
|
||||
if (number) {
|
||||
row = objectSchema.table->find_first_int(primaryProperty.column, number.longLongValue);
|
||||
}
|
||||
else if (!key && primaryProperty.optional) {
|
||||
row = objectSchema.table->find_first_null(primaryProperty.column);
|
||||
}
|
||||
else {
|
||||
@throw RLMException(@"Invalid value '%@' for primary key", key);
|
||||
}
|
||||
}
|
||||
|
||||
if (row == realm::not_found) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return RLMCreateObjectAccessor(realm, objectSchema, row);
|
||||
}
|
||||
|
||||
RLMObjectBase *RLMCreateObjectAccessor(__unsafe_unretained RLMRealm *const realm,
|
||||
__unsafe_unretained RLMObjectSchema *const objectSchema,
|
||||
NSUInteger index) {
|
||||
return RLMCreateObjectAccessor(realm, objectSchema, (*objectSchema.table)[index]);
|
||||
}
|
||||
|
||||
// Create accessor and register with realm
|
||||
RLMObjectBase *RLMCreateObjectAccessor(__unsafe_unretained RLMRealm *const realm,
|
||||
__unsafe_unretained RLMObjectSchema *const objectSchema,
|
||||
realm::RowExpr row) {
|
||||
RLMObjectBase *accessor = [[objectSchema.accessorClass alloc] initWithRealm:realm schema:objectSchema];
|
||||
accessor->_row = row;
|
||||
RLMInitializeSwiftAccessorGenerics(accessor);
|
||||
return accessor;
|
||||
}
|
||||
484
Example/Pods/Realm/Realm/RLMObservation.mm
generated
Normal file
484
Example/Pods/Realm/Realm/RLMObservation.mm
generated
Normal file
@@ -0,0 +1,484 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2015 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 "RLMObservation.hpp"
|
||||
|
||||
#import "RLMAccessor.h"
|
||||
#import "RLMArray_Private.hpp"
|
||||
#import "RLMListBase.h"
|
||||
#import "RLMObjectSchema_Private.hpp"
|
||||
#import "RLMProperty_Private.h"
|
||||
#import "RLMRealm_Private.hpp"
|
||||
#import "RLMSchema.h"
|
||||
|
||||
#import <realm/lang_bind_helper.hpp>
|
||||
|
||||
using namespace realm;
|
||||
|
||||
namespace {
|
||||
template<typename Iterator>
|
||||
struct IteratorPair {
|
||||
Iterator first;
|
||||
Iterator second;
|
||||
};
|
||||
template<typename Iterator>
|
||||
Iterator begin(IteratorPair<Iterator> const& p) {
|
||||
return p.first;
|
||||
}
|
||||
template<typename Iterator>
|
||||
Iterator end(IteratorPair<Iterator> const& p) {
|
||||
return p.second;
|
||||
}
|
||||
|
||||
template<typename Container>
|
||||
auto reverse(Container const& c) {
|
||||
return IteratorPair<typename Container::const_reverse_iterator>{c.rbegin(), c.rend()};
|
||||
}
|
||||
}
|
||||
|
||||
RLMObservationInfo::RLMObservationInfo(RLMObjectSchema *objectSchema, std::size_t row, id object)
|
||||
: object(object)
|
||||
, objectSchema(objectSchema)
|
||||
{
|
||||
REALM_ASSERT_DEBUG(objectSchema);
|
||||
setRow(*objectSchema.table, row);
|
||||
}
|
||||
|
||||
RLMObservationInfo::RLMObservationInfo(id object)
|
||||
: object(object)
|
||||
{
|
||||
}
|
||||
|
||||
RLMObservationInfo::~RLMObservationInfo() {
|
||||
if (prev) {
|
||||
// Not the head of the linked list, so just detach from the list
|
||||
REALM_ASSERT_DEBUG(prev->next == this);
|
||||
prev->next = next;
|
||||
if (next) {
|
||||
REALM_ASSERT_DEBUG(next->prev == this);
|
||||
next->prev = prev;
|
||||
}
|
||||
}
|
||||
else if (objectSchema) {
|
||||
// The head of the list, so remove self from the object schema's array
|
||||
// of observation info, either replacing self with the next info or
|
||||
// removing entirely if there is no next
|
||||
auto end = objectSchema->_observedObjects.end();
|
||||
auto it = find(objectSchema->_observedObjects.begin(), end, this);
|
||||
if (it != end) {
|
||||
if (next) {
|
||||
*it = next;
|
||||
next->prev = nullptr;
|
||||
}
|
||||
else {
|
||||
iter_swap(it, std::prev(end));
|
||||
objectSchema->_observedObjects.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Otherwise the observed object was standalone, so nothing to do
|
||||
|
||||
#ifdef DEBUG
|
||||
// ensure that incorrect cleanup fails noisily
|
||||
object = (__bridge id)(void *)-1;
|
||||
prev = (RLMObservationInfo *)-1;
|
||||
next = (RLMObservationInfo *)-1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void RLMObservationInfo::willChange(NSString *key, NSKeyValueChange kind, NSIndexSet *indexes) const {
|
||||
if (indexes) {
|
||||
forEach([=](__unsafe_unretained auto o) {
|
||||
[o willChange:kind valuesAtIndexes:indexes forKey:key];
|
||||
});
|
||||
}
|
||||
else {
|
||||
forEach([=](__unsafe_unretained auto o) {
|
||||
[o willChangeValueForKey:key];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void RLMObservationInfo::didChange(NSString *key, NSKeyValueChange kind, NSIndexSet *indexes) const {
|
||||
if (indexes) {
|
||||
forEach([=](__unsafe_unretained auto o) {
|
||||
[o didChange:kind valuesAtIndexes:indexes forKey:key];
|
||||
});
|
||||
}
|
||||
else {
|
||||
forEach([=](__unsafe_unretained auto o) {
|
||||
[o didChangeValueForKey:key];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void RLMObservationInfo::prepareForInvalidation() {
|
||||
REALM_ASSERT_DEBUG(objectSchema);
|
||||
REALM_ASSERT_DEBUG(!prev);
|
||||
for (auto info = this; info; info = info->next)
|
||||
info->invalidated = true;
|
||||
}
|
||||
|
||||
void RLMObservationInfo::setRow(realm::Table &table, size_t newRow) {
|
||||
REALM_ASSERT_DEBUG(!row);
|
||||
REALM_ASSERT_DEBUG(objectSchema);
|
||||
row = table[newRow];
|
||||
for (auto info : objectSchema->_observedObjects) {
|
||||
if (info->row && info->row.get_index() == row.get_index()) {
|
||||
prev = info;
|
||||
next = info->next;
|
||||
if (next)
|
||||
next->prev = this;
|
||||
info->next = this;
|
||||
return;
|
||||
}
|
||||
}
|
||||
objectSchema->_observedObjects.push_back(this);
|
||||
}
|
||||
|
||||
void RLMObservationInfo::recordObserver(realm::Row& objectRow,
|
||||
__unsafe_unretained RLMObjectSchema *const objectSchema,
|
||||
__unsafe_unretained NSString *const keyPath) {
|
||||
++observerCount;
|
||||
|
||||
// add ourselves to the list of observed objects if this is the first time
|
||||
// an observer is being added to a persisted object
|
||||
if (objectRow && !row) {
|
||||
this->objectSchema = objectSchema;
|
||||
setRow(*objectRow.get_table(), objectRow.get_index());
|
||||
}
|
||||
|
||||
if (!row) {
|
||||
// Arrays need a reference to their containing object to avoid having to
|
||||
// go through the awful proxy object from mutableArrayValueForKey.
|
||||
// For persisted objects we do this when the object is added or created
|
||||
// (and have to to support notifications from modifying an object which
|
||||
// was never observed), but for Swift classes (both RealmSwift and
|
||||
// RLMObject) we can't do it then because we don't know what the parent
|
||||
// object is.
|
||||
|
||||
NSUInteger sep = [keyPath rangeOfString:@"."].location;
|
||||
NSString *key = sep == NSNotFound ? keyPath : [keyPath substringToIndex:sep];
|
||||
RLMProperty *prop = objectSchema[key];
|
||||
if (prop && prop.type == RLMPropertyTypeArray) {
|
||||
id value = valueForKey(key);
|
||||
RLMArray *array = [value isKindOfClass:[RLMListBase class]] ? [value _rlmArray] : value;
|
||||
array->_key = key;
|
||||
array->_parentObject = object;
|
||||
}
|
||||
else if (auto swiftIvar = prop.swiftIvar) {
|
||||
if (auto optional = RLMDynamicCast<RLMOptionalBase>(object_getIvar(object, swiftIvar))) {
|
||||
optional.property = prop;
|
||||
optional.object = object;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RLMObservationInfo::removeObserver() {
|
||||
--observerCount;
|
||||
}
|
||||
|
||||
id RLMObservationInfo::valueForKey(NSString *key) {
|
||||
if (invalidated) {
|
||||
if ([key isEqualToString:RLMInvalidatedKey]) {
|
||||
return @YES;
|
||||
}
|
||||
return cachedObjects[key];
|
||||
}
|
||||
|
||||
if (key != lastKey) {
|
||||
lastKey = key;
|
||||
lastProp = objectSchema[key];
|
||||
}
|
||||
|
||||
static auto superValueForKey = reinterpret_cast<id(*)(id, SEL, NSString *)>([NSObject methodForSelector:@selector(valueForKey:)]);
|
||||
if (!lastProp) {
|
||||
return RLMCoerceToNil(superValueForKey(object, @selector(valueForKey:), key));
|
||||
}
|
||||
|
||||
auto getSuper = [&] {
|
||||
return row ? RLMDynamicGet(object, lastProp) : RLMCoerceToNil(superValueForKey(object, @selector(valueForKey:), key));
|
||||
};
|
||||
|
||||
// We need to return the same object each time for observing over keypaths to work
|
||||
if (lastProp.type == RLMPropertyTypeArray) {
|
||||
RLMArray *value = cachedObjects[key];
|
||||
if (!value) {
|
||||
value = getSuper();
|
||||
if (!cachedObjects) {
|
||||
cachedObjects = [NSMutableDictionary new];
|
||||
}
|
||||
cachedObjects[key] = value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
if (lastProp.type == RLMPropertyTypeObject) {
|
||||
if (row.is_null_link(lastProp.column)) {
|
||||
[cachedObjects removeObjectForKey:key];
|
||||
return nil;
|
||||
}
|
||||
|
||||
RLMObjectBase *value = cachedObjects[key];
|
||||
if (value && value->_row.get_index() == row.get_link(lastProp.column)) {
|
||||
return value;
|
||||
}
|
||||
value = getSuper();
|
||||
if (!cachedObjects) {
|
||||
cachedObjects = [NSMutableDictionary new];
|
||||
}
|
||||
cachedObjects[key] = value;
|
||||
return value;
|
||||
}
|
||||
|
||||
return getSuper();
|
||||
}
|
||||
|
||||
RLMObservationInfo *RLMGetObservationInfo(RLMObservationInfo *info,
|
||||
size_t row,
|
||||
__unsafe_unretained RLMObjectSchema *objectSchema) {
|
||||
if (info) {
|
||||
return info;
|
||||
}
|
||||
|
||||
for (RLMObservationInfo *info : objectSchema->_observedObjects) {
|
||||
if (info->isForRow(row)) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void RLMClearTable(RLMObjectSchema *objectSchema) {
|
||||
for (auto info : objectSchema->_observedObjects) {
|
||||
info->willChange(RLMInvalidatedKey);
|
||||
}
|
||||
|
||||
RLMTrackDeletions(objectSchema.realm, ^{
|
||||
objectSchema.table->clear();
|
||||
|
||||
for (auto info : objectSchema->_observedObjects) {
|
||||
info->prepareForInvalidation();
|
||||
}
|
||||
});
|
||||
|
||||
for (auto info : reverse(objectSchema->_observedObjects)) {
|
||||
info->didChange(RLMInvalidatedKey);
|
||||
}
|
||||
|
||||
objectSchema->_observedObjects.clear();
|
||||
}
|
||||
|
||||
void RLMTrackDeletions(__unsafe_unretained RLMRealm *const realm, dispatch_block_t block) {
|
||||
std::vector<std::vector<RLMObservationInfo *> *> observers;
|
||||
|
||||
// Build up an array of observation info arrays which is indexed by table
|
||||
// index (the object schemata may be in an entirely different order)
|
||||
for (RLMObjectSchema *objectSchema in realm.schema.objectSchema) {
|
||||
if (objectSchema->_observedObjects.empty()) {
|
||||
continue;
|
||||
}
|
||||
size_t ndx = objectSchema.table->get_index_in_group();
|
||||
if (ndx >= observers.size()) {
|
||||
observers.resize(std::max(observers.size() * 2, ndx + 1));
|
||||
}
|
||||
observers[ndx] = &objectSchema->_observedObjects;
|
||||
}
|
||||
|
||||
// No need for change tracking if no objects are observed
|
||||
if (observers.empty()) {
|
||||
block();
|
||||
return;
|
||||
}
|
||||
|
||||
struct change {
|
||||
RLMObservationInfo *info;
|
||||
__unsafe_unretained NSString *property;
|
||||
NSMutableIndexSet *indexes;
|
||||
};
|
||||
|
||||
std::vector<change> changes;
|
||||
std::vector<RLMObservationInfo *> invalidated;
|
||||
|
||||
// This callback is called by core with a list of row deletions and
|
||||
// resulting link nullifications immediately before things are deleted and nullified
|
||||
realm.group->set_cascade_notification_handler([&](realm::Group::CascadeNotification const& cs) {
|
||||
for (auto const& link : cs.links) {
|
||||
size_t table_ndx = link.origin_table->get_index_in_group();
|
||||
if (table_ndx >= observers.size() || !observers[table_ndx]) {
|
||||
// The modified table has no observers
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto observer : *observers[table_ndx]) {
|
||||
if (!observer->isForRow(link.origin_row_ndx)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RLMProperty *prop = observer->getObjectSchema().properties[link.origin_col_ndx];
|
||||
NSString *name = prop.name;
|
||||
if (prop.type != RLMPropertyTypeArray) {
|
||||
changes.push_back({observer, name});
|
||||
continue;
|
||||
}
|
||||
|
||||
auto c = find_if(begin(changes), end(changes), [&](auto const& c) {
|
||||
return c.info == observer && c.property == name;
|
||||
});
|
||||
if (c == end(changes)) {
|
||||
changes.push_back({observer, name, [NSMutableIndexSet new]});
|
||||
c = prev(end(changes));
|
||||
}
|
||||
|
||||
// We know what row index is being removed from the LinkView,
|
||||
// but what we actually want is the indexes in the LinkView that
|
||||
// are going away
|
||||
auto linkview = observer->getRow().get_linklist(prop.column);
|
||||
size_t start = 0, index;
|
||||
while ((index = linkview->find(link.old_target_row_ndx, start)) != realm::not_found) {
|
||||
[c->indexes addIndex:index];
|
||||
start = index + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const& row : cs.rows) {
|
||||
if (row.table_ndx >= observers.size() || !observers[row.table_ndx]) {
|
||||
// The modified table has no observers
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto observer : *observers[row.table_ndx]) {
|
||||
if (observer->isForRow(row.row_ndx)) {
|
||||
invalidated.push_back(observer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The relative order of these loops is very important
|
||||
for (auto info : invalidated) {
|
||||
info->willChange(RLMInvalidatedKey);
|
||||
}
|
||||
for (auto const& change : changes) {
|
||||
change.info->willChange(change.property, NSKeyValueChangeRemoval, change.indexes);
|
||||
}
|
||||
for (auto info : invalidated) {
|
||||
info->prepareForInvalidation();
|
||||
}
|
||||
});
|
||||
|
||||
block();
|
||||
|
||||
for (auto const& change : reverse(changes)) {
|
||||
change.info->didChange(change.property, NSKeyValueChangeRemoval, change.indexes);
|
||||
}
|
||||
for (auto info : reverse(invalidated)) {
|
||||
info->didChange(RLMInvalidatedKey);
|
||||
}
|
||||
|
||||
realm.group->set_cascade_notification_handler(nullptr);
|
||||
}
|
||||
|
||||
namespace {
|
||||
template<typename Func>
|
||||
void forEach(realm::BindingContext::ObserverState const& state, Func&& func) {
|
||||
for (size_t i = 0, size = state.changes.size(); i < size; ++i) {
|
||||
if (state.changes[i].changed) {
|
||||
func(i, state.changes[i], static_cast<RLMObservationInfo *>(state.info));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<realm::BindingContext::ObserverState> RLMGetObservedRows(NSArray RLM_GENERIC(RLMObjectSchema *) *schema) {
|
||||
std::vector<realm::BindingContext::ObserverState> observers;
|
||||
for (RLMObjectSchema *objectSchema in schema) {
|
||||
for (auto info : objectSchema->_observedObjects) {
|
||||
auto const& row = info->getRow();
|
||||
if (!row.is_attached())
|
||||
continue;
|
||||
observers.push_back({
|
||||
row.get_table()->get_index_in_group(),
|
||||
row.get_index(),
|
||||
info});
|
||||
}
|
||||
}
|
||||
sort(begin(observers), end(observers));
|
||||
return observers;
|
||||
}
|
||||
|
||||
static NSKeyValueChange convert(realm::BindingContext::ColumnInfo::Kind kind) {
|
||||
switch (kind) {
|
||||
case realm::BindingContext::ColumnInfo::Kind::None:
|
||||
case realm::BindingContext::ColumnInfo::Kind::SetAll:
|
||||
return NSKeyValueChangeSetting;
|
||||
case realm::BindingContext::ColumnInfo::Kind::Set:
|
||||
return NSKeyValueChangeReplacement;
|
||||
case realm::BindingContext::ColumnInfo::Kind::Insert:
|
||||
return NSKeyValueChangeInsertion;
|
||||
case realm::BindingContext::ColumnInfo::Kind::Remove:
|
||||
return NSKeyValueChangeRemoval;
|
||||
}
|
||||
}
|
||||
|
||||
static NSIndexSet *convert(realm::IndexSet const& in, NSMutableIndexSet *out) {
|
||||
if (in.empty()) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
[out removeAllIndexes];
|
||||
for (auto range : in) {
|
||||
[out addIndexesInRange:{range.first, range.second - range.first}];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void RLMWillChange(std::vector<realm::BindingContext::ObserverState> const& observed,
|
||||
std::vector<void *> const& invalidated) {
|
||||
NSMutableIndexSet *indexes = [NSMutableIndexSet new];
|
||||
for (auto info : invalidated) {
|
||||
static_cast<RLMObservationInfo *>(info)->willChange(RLMInvalidatedKey);
|
||||
}
|
||||
for (auto const& o : observed) {
|
||||
forEach(o, [&](size_t i, auto const& change, RLMObservationInfo *info) {
|
||||
info->willChange([info->getObjectSchema().properties[i] name],
|
||||
convert(change.kind), convert(change.indices, indexes));
|
||||
});
|
||||
}
|
||||
for (auto info : invalidated) {
|
||||
static_cast<RLMObservationInfo *>(info)->prepareForInvalidation();
|
||||
}
|
||||
}
|
||||
|
||||
void RLMDidChange(std::vector<realm::BindingContext::ObserverState> const& observed,
|
||||
std::vector<void *> const& invalidated) {
|
||||
// Loop in reverse order to avoid O(N^2) behavior in Foundation
|
||||
NSMutableIndexSet *indexes = [NSMutableIndexSet new];
|
||||
for (auto const& o : reverse(observed)) {
|
||||
forEach(o, [&](size_t i, auto const& change, RLMObservationInfo *info) {
|
||||
info->didChange([info->getObjectSchema().properties[i] name],
|
||||
convert(change.kind), convert(change.indices, indexes));
|
||||
});
|
||||
}
|
||||
for (auto const& info : reverse(invalidated)) {
|
||||
static_cast<RLMObservationInfo *>(info)->didChange(RLMInvalidatedKey);
|
||||
}
|
||||
}
|
||||
86
Example/Pods/Realm/Realm/RLMOptionalBase.mm
generated
Normal file
86
Example/Pods/Realm/Realm/RLMOptionalBase.mm
generated
Normal file
@@ -0,0 +1,86 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2015 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 "RLMAccessor.h"
|
||||
#import "RLMOptionalBase.h"
|
||||
#import "RLMObject_Private.h"
|
||||
#import "RLMObjectStore.h"
|
||||
#import "RLMProperty.h"
|
||||
#import "RLMUtil.hpp"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@interface RLMOptionalBase ()
|
||||
@property (nonatomic) id standaloneValue;
|
||||
@end
|
||||
|
||||
@implementation RLMOptionalBase
|
||||
|
||||
- (instancetype)init {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)underlyingValue {
|
||||
if ((_object && _object->_realm) || _object.isInvalidated) {
|
||||
return RLMDynamicGet(_object, _property);
|
||||
}
|
||||
else {
|
||||
return _standaloneValue;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setUnderlyingValue:(id)underlyingValue {
|
||||
if ((_object && _object->_realm) || _object.isInvalidated) {
|
||||
RLMDynamicSet(_object, _property, underlyingValue, RLMCreationOptionsNone);
|
||||
}
|
||||
else {
|
||||
NSString *propertyName = _property.name;
|
||||
[_object willChangeValueForKey:propertyName];
|
||||
_standaloneValue = underlyingValue;
|
||||
[_object didChangeValueForKey:propertyName];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isKindOfClass:(Class)aClass {
|
||||
return [self.underlyingValue isKindOfClass:aClass] || RLMIsKindOfClass(object_getClass(self), aClass);
|
||||
}
|
||||
|
||||
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
|
||||
return [self.underlyingValue methodSignatureForSelector:sel];
|
||||
}
|
||||
|
||||
- (void)forwardInvocation:(NSInvocation *)invocation {
|
||||
[invocation invokeWithTarget:self.underlyingValue];
|
||||
}
|
||||
|
||||
- (id)forwardingTargetForSelector:(__unused SEL)sel {
|
||||
return self.underlyingValue;
|
||||
}
|
||||
|
||||
- (BOOL)respondsToSelector:(SEL)aSelector {
|
||||
if (id val = self.underlyingValue) {
|
||||
return [val respondsToSelector:aSelector];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)doesNotRecognizeSelector:(SEL)aSelector {
|
||||
[self.underlyingValue doesNotRecognizeSelector:aSelector];
|
||||
}
|
||||
|
||||
@end
|
||||
118
Example/Pods/Realm/Realm/RLMPredicateUtil.mm
generated
Normal file
118
Example/Pods/Realm/Realm/RLMPredicateUtil.mm
generated
Normal file
@@ -0,0 +1,118 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2015 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 "RLMPredicateUtil.hpp"
|
||||
|
||||
// NSConditionalExpressionType is new in OS X 10.11 and iOS 9.0
|
||||
#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
|
||||
#define CONDITIONAL_EXPRESSION_DECLARED (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
|
||||
#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
|
||||
#define CONDITIONAL_EXPRESSION_DECLARED (__IPHONE_OS_VERSION_MIN_REQUIRED >= 90000)
|
||||
#else
|
||||
#define CONDITIONAL_EXPRESSION_DECLARED 0
|
||||
#endif
|
||||
|
||||
#if !CONDITIONAL_EXPRESSION_DECLARED
|
||||
|
||||
#define NSConditionalExpressionType 20
|
||||
|
||||
@interface NSExpression (NewIn1011And90)
|
||||
+ (NSExpression *)expressionForConditional:(NSPredicate *)predicate trueExpression:(NSExpression *)trueExpression falseExpression:(NSExpression *)falseExpression;
|
||||
- (NSExpression *)trueExpression;
|
||||
- (NSExpression *)falseExpression;
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
struct PredicateExpressionTransformer {
|
||||
PredicateExpressionTransformer(ExpressionVisitor visitor) : m_visitor(visitor) { }
|
||||
|
||||
NSExpression *visit(NSExpression *expression) const;
|
||||
NSPredicate *visit(NSPredicate *predicate) const;
|
||||
|
||||
ExpressionVisitor m_visitor;
|
||||
};
|
||||
|
||||
NSExpression *PredicateExpressionTransformer::visit(NSExpression *expression) const {
|
||||
expression = m_visitor(expression);
|
||||
|
||||
switch (expression.expressionType) {
|
||||
case NSFunctionExpressionType: {
|
||||
NSMutableArray *arguments = [NSMutableArray array];
|
||||
for (NSExpression *argument in expression.arguments) {
|
||||
[arguments addObject:visit(argument)];
|
||||
}
|
||||
if (expression.operand) {
|
||||
return [NSExpression expressionForFunction:visit(expression.operand) selectorName:expression.function arguments:arguments];
|
||||
} else {
|
||||
return [NSExpression expressionForFunction:expression.function arguments:arguments];
|
||||
}
|
||||
}
|
||||
|
||||
case NSUnionSetExpressionType:
|
||||
return [NSExpression expressionForUnionSet:visit(expression.leftExpression) with:visit(expression.rightExpression)];
|
||||
case NSIntersectSetExpressionType:
|
||||
return [NSExpression expressionForIntersectSet:visit(expression.leftExpression) with:visit(expression.rightExpression)];
|
||||
case NSMinusSetExpressionType:
|
||||
return [NSExpression expressionForMinusSet:visit(expression.leftExpression) with:visit(expression.rightExpression)];
|
||||
|
||||
case NSSubqueryExpressionType:
|
||||
return [NSExpression expressionForSubquery:visit(expression.operand) usingIteratorVariable:expression.variable predicate:visit(expression.predicate)];
|
||||
|
||||
case NSAggregateExpressionType: {
|
||||
NSMutableArray *subexpressions = [NSMutableArray array];
|
||||
for (NSExpression *subexpression in expression.collection) {
|
||||
[subexpressions addObject:visit(subexpression)];
|
||||
}
|
||||
return [NSExpression expressionForAggregate:subexpressions];
|
||||
}
|
||||
|
||||
case NSConditionalExpressionType:
|
||||
return [NSExpression expressionForConditional:visit(expression.predicate) trueExpression:visit(expression.trueExpression) falseExpression:visit(expression.falseExpression)];
|
||||
|
||||
default:
|
||||
// The remaining expression types do not contain nested expressions or predicates.
|
||||
return expression;
|
||||
}
|
||||
}
|
||||
|
||||
NSPredicate *PredicateExpressionTransformer::visit(NSPredicate *predicate) const {
|
||||
if ([predicate isKindOfClass:[NSCompoundPredicate class]]) {
|
||||
NSCompoundPredicate *compoundPredicate = (NSCompoundPredicate *)predicate;
|
||||
NSMutableArray *subpredicates = [NSMutableArray array];
|
||||
for (NSPredicate *subpredicate in compoundPredicate.subpredicates) {
|
||||
[subpredicates addObject:visit(subpredicate)];
|
||||
}
|
||||
return [[NSCompoundPredicate alloc] initWithType:compoundPredicate.compoundPredicateType subpredicates:subpredicates];
|
||||
}
|
||||
if ([predicate isKindOfClass:[NSComparisonPredicate class]]) {
|
||||
NSComparisonPredicate *comparisonPredicate = (NSComparisonPredicate *)predicate;
|
||||
NSExpression *leftExpression = visit(comparisonPredicate.leftExpression);
|
||||
NSExpression *rightExpression = visit(comparisonPredicate.rightExpression);
|
||||
return [NSComparisonPredicate predicateWithLeftExpression:leftExpression rightExpression:rightExpression modifier:comparisonPredicate.comparisonPredicateModifier type:comparisonPredicate.predicateOperatorType options:comparisonPredicate.options];
|
||||
}
|
||||
return predicate;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
NSPredicate *transformPredicate(NSPredicate *predicate, ExpressionVisitor visitor) {
|
||||
PredicateExpressionTransformer transformer(visitor);
|
||||
return transformer.visit(predicate);
|
||||
}
|
||||
434
Example/Pods/Realm/Realm/RLMProperty.mm
generated
Normal file
434
Example/Pods/Realm/Realm/RLMProperty.mm
generated
Normal file
@@ -0,0 +1,434 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RLMProperty_Private.h"
|
||||
|
||||
#import "RLMArray.h"
|
||||
#import "RLMListBase.h"
|
||||
#import "RLMObject.h"
|
||||
#import "RLMObject_Private.h"
|
||||
#import "RLMOptionalBase.h"
|
||||
#import "RLMSchema_Private.h"
|
||||
#import "RLMSwiftSupport.h"
|
||||
#import "RLMUtil.hpp"
|
||||
|
||||
BOOL RLMPropertyTypeIsNullable(RLMPropertyType propertyType) {
|
||||
return propertyType != RLMPropertyTypeAny && propertyType != RLMPropertyTypeArray;
|
||||
}
|
||||
|
||||
BOOL RLMPropertyTypeIsNumeric(RLMPropertyType propertyType) {
|
||||
switch (propertyType) {
|
||||
case RLMPropertyTypeInt:
|
||||
case RLMPropertyTypeFloat:
|
||||
case RLMPropertyTypeDouble:
|
||||
return YES;
|
||||
default:
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
@implementation RLMProperty
|
||||
- (instancetype)initWithName:(NSString *)name
|
||||
type:(RLMPropertyType)type
|
||||
objectClassName:(NSString *)objectClassName
|
||||
indexed:(BOOL)indexed
|
||||
optional:(BOOL)optional {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_name = name;
|
||||
_type = type;
|
||||
_objectClassName = objectClassName;
|
||||
_indexed = indexed;
|
||||
_optional = optional;
|
||||
[self setObjcCodeFromType];
|
||||
[self updateAccessors];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)updateAccessors {
|
||||
// populate getter/setter names if generic
|
||||
if (!_getterName) {
|
||||
_getterName = _name;
|
||||
}
|
||||
if (!_setterName) {
|
||||
// Objective-C setters only capitalize the first letter of the property name if it falls between 'a' and 'z'
|
||||
int asciiCode = [_name characterAtIndex:0];
|
||||
BOOL shouldUppercase = asciiCode >= 'a' && asciiCode <= 'z';
|
||||
NSString *firstChar = [_name substringToIndex:1];
|
||||
firstChar = shouldUppercase ? firstChar.uppercaseString : firstChar;
|
||||
_setterName = [NSString stringWithFormat:@"set%@%@:", firstChar, [_name substringFromIndex:1]];
|
||||
}
|
||||
|
||||
_getterSel = NSSelectorFromString(_getterName);
|
||||
_setterSel = NSSelectorFromString(_setterName);
|
||||
}
|
||||
|
||||
-(void)setObjcCodeFromType {
|
||||
if (_optional) {
|
||||
_objcType = '@';
|
||||
}
|
||||
switch (_type) {
|
||||
case RLMPropertyTypeInt:
|
||||
_objcType = 'q';
|
||||
break;
|
||||
case RLMPropertyTypeBool:
|
||||
_objcType = 'c';
|
||||
break;
|
||||
case RLMPropertyTypeDouble:
|
||||
_objcType = 'd';
|
||||
break;
|
||||
case RLMPropertyTypeFloat:
|
||||
_objcType = 'f';
|
||||
break;
|
||||
case RLMPropertyTypeAny:
|
||||
case RLMPropertyTypeArray:
|
||||
case RLMPropertyTypeData:
|
||||
case RLMPropertyTypeDate:
|
||||
case RLMPropertyTypeObject:
|
||||
case RLMPropertyTypeString:
|
||||
_objcType = '@';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// determine RLMPropertyType from objc code - returns true if valid type was found/set
|
||||
- (BOOL)setTypeFromRawType {
|
||||
const char *code = _objcRawType.UTF8String;
|
||||
_objcType = *code; // first char of type attr
|
||||
|
||||
// map to RLMPropertyType
|
||||
switch (self.objcType) {
|
||||
case 's': // short
|
||||
case 'i': // int
|
||||
case 'l': // long
|
||||
case 'q': // long long
|
||||
_type = RLMPropertyTypeInt;
|
||||
return YES;
|
||||
case 'f':
|
||||
_type = RLMPropertyTypeFloat;
|
||||
return YES;
|
||||
case 'd':
|
||||
_type = RLMPropertyTypeDouble;
|
||||
return YES;
|
||||
case 'c': // BOOL is stored as char - since rlm has no char type this is ok
|
||||
case 'B':
|
||||
_type = RLMPropertyTypeBool;
|
||||
return YES;
|
||||
case '@': {
|
||||
_optional = true;
|
||||
static const char arrayPrefix[] = "@\"RLMArray<";
|
||||
static const int arrayPrefixLen = sizeof(arrayPrefix) - 1;
|
||||
|
||||
static const char numberPrefix[] = "@\"NSNumber<";
|
||||
static const int numberPrefixLen = sizeof(numberPrefix) - 1;
|
||||
|
||||
if (code[1] == '\0') {
|
||||
// string is "@"
|
||||
_type = RLMPropertyTypeAny;
|
||||
_optional = false;
|
||||
}
|
||||
else if (strcmp(code, "@\"NSString\"") == 0) {
|
||||
_type = RLMPropertyTypeString;
|
||||
}
|
||||
else if (strcmp(code, "@\"NSDate\"") == 0) {
|
||||
_type = RLMPropertyTypeDate;
|
||||
}
|
||||
else if (strcmp(code, "@\"NSData\"") == 0) {
|
||||
_type = RLMPropertyTypeData;
|
||||
}
|
||||
else if (strncmp(code, arrayPrefix, arrayPrefixLen) == 0) {
|
||||
_optional = false;
|
||||
// get object class from type string - @"RLMArray<objectClassName>"
|
||||
_type = RLMPropertyTypeArray;
|
||||
_objectClassName = [[NSString alloc] initWithBytes:code + arrayPrefixLen
|
||||
length:strlen(code + arrayPrefixLen) - 2 // drop trailing >"
|
||||
encoding:NSUTF8StringEncoding];
|
||||
|
||||
Class cls = [RLMSchema classForString:_objectClassName];
|
||||
if (!cls) {
|
||||
@throw RLMException(@"Property '%@' is of type 'RLMArray<%@>' which is not a supported RLMArray object type. "
|
||||
@"RLMArrays can only contain instances of RLMObject subclasses. "
|
||||
@"See https://realm.io/docs/objc/latest/#to-many for more information.", _name, _objectClassName);
|
||||
}
|
||||
}
|
||||
else if (strncmp(code, numberPrefix, numberPrefixLen) == 0) {
|
||||
// get number type from type string - @"NSNumber<objectClassName>"
|
||||
NSString *numberType = [[NSString alloc] initWithBytes:code + numberPrefixLen
|
||||
length:strlen(code + numberPrefixLen) - 2 // drop trailing >"
|
||||
encoding:NSUTF8StringEncoding];
|
||||
|
||||
if ([numberType isEqualToString:@"RLMInt"]) {
|
||||
_type = RLMPropertyTypeInt;
|
||||
}
|
||||
else if ([numberType isEqualToString:@"RLMFloat"]) {
|
||||
_type = RLMPropertyTypeFloat;
|
||||
}
|
||||
else if ([numberType isEqualToString:@"RLMDouble"]) {
|
||||
_type = RLMPropertyTypeDouble;
|
||||
}
|
||||
else if ([numberType isEqualToString:@"RLMBool"]) {
|
||||
_type = RLMPropertyTypeBool;
|
||||
}
|
||||
else {
|
||||
@throw RLMException(@"Property '%@' is of type 'NSNumber<%@>' which is not a supported NSNumber object type. "
|
||||
@"NSNumbers can only be RLMInt, RLMFloat, RLMDouble, and RLMBool at the moment. "
|
||||
@"See https://realm.io/docs/objc/latest for more information.", _name, numberType);
|
||||
}
|
||||
}
|
||||
else if (strcmp(code, "@\"NSNumber\"") == 0) {
|
||||
@throw RLMException(@"Property '%@' requires a protocol defining the contained type - example: NSNumber<RLMInt>.", _name);
|
||||
}
|
||||
else if (strcmp(code, "@\"RLMArray\"") == 0) {
|
||||
@throw RLMException(@"Property '%@' requires a protocol defining the contained type - example: RLMArray<Person>.", _name);
|
||||
}
|
||||
else {
|
||||
// for objects strip the quotes and @
|
||||
NSString *className = [_objcRawType substringWithRange:NSMakeRange(2, _objcRawType.length-3)];
|
||||
|
||||
// verify type
|
||||
Class cls = [RLMSchema classForString:className];
|
||||
if (!cls) {
|
||||
@throw RLMException(@"Property '%@' is declared as '%@', which is not a supported RLMObject property type. "
|
||||
@"All properties must be primitives, NSString, NSDate, NSData, NSNumber, RLMArray, or subclasses of RLMObject. "
|
||||
@"See https://realm.io/docs/objc/latest/api/Classes/RLMObject.html for more information.", _name, className);
|
||||
}
|
||||
|
||||
_type = RLMPropertyTypeObject;
|
||||
_optional = true;
|
||||
_objectClassName = [cls className] ?: className;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
default:
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (bool)parseObjcProperty:(objc_property_t)property {
|
||||
unsigned int count;
|
||||
objc_property_attribute_t *attrs = property_copyAttributeList(property, &count);
|
||||
|
||||
bool ignore = false;
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
switch (*attrs[i].name) {
|
||||
case 'T':
|
||||
_objcRawType = @(attrs[i].value);
|
||||
break;
|
||||
case 'R':
|
||||
ignore = true;
|
||||
break;
|
||||
case 'N':
|
||||
// nonatomic
|
||||
break;
|
||||
case 'D':
|
||||
// dynamic
|
||||
break;
|
||||
case 'G':
|
||||
_getterName = @(attrs[i].value);
|
||||
break;
|
||||
case 'S':
|
||||
_setterName = @(attrs[i].value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(attrs);
|
||||
|
||||
return ignore;
|
||||
}
|
||||
|
||||
- (instancetype)initSwiftPropertyWithName:(NSString *)name
|
||||
indexed:(BOOL)indexed
|
||||
property:(objc_property_t)property
|
||||
instance:(RLMObject *)obj {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_name = name;
|
||||
_indexed = indexed;
|
||||
|
||||
if ([self parseObjcProperty:property]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
id propertyValue = [obj valueForKey:_name];
|
||||
|
||||
// convert array types to objc variant
|
||||
if ([_objcRawType isEqualToString:@"@\"RLMArray\""]) {
|
||||
_objcRawType = [NSString stringWithFormat:@"@\"RLMArray<%@>\"", [propertyValue objectClassName]];
|
||||
}
|
||||
else if ([_objcRawType isEqualToString:@"@\"NSNumber\""]) {
|
||||
const char *numberType = [propertyValue objCType];
|
||||
switch (*numberType) {
|
||||
case 'i':
|
||||
case 'l':
|
||||
case 'q':
|
||||
_objcRawType = @"@\"NSNumber<RLMInt>\"";
|
||||
break;
|
||||
case 'f':
|
||||
_objcRawType = @"@\"NSNumber<RLMFloat>\"";
|
||||
break;
|
||||
case 'd':
|
||||
_objcRawType = @"@\"NSNumber<RLMDouble>\"";
|
||||
break;
|
||||
case 'B':
|
||||
case 'c':
|
||||
_objcRawType = @"@\"NSNumber<RLMBool>\"";
|
||||
break;
|
||||
default:
|
||||
@throw RLMException(@"Can't persist NSNumber of type '%s': only integers, floats, doubles, and bools are currently supported.", numberType);
|
||||
}
|
||||
}
|
||||
|
||||
void (^throwForPropertyName)(NSString *) = ^(NSString *propertyName){
|
||||
@throw RLMException(@"Can't persist property '%@' with incompatible type. "
|
||||
"Add to Object.ignoredProperties() class method to ignore.", propertyName);
|
||||
};
|
||||
|
||||
if (![self setTypeFromRawType]) {
|
||||
throwForPropertyName(self.name);
|
||||
}
|
||||
|
||||
if (_type == RLMPropertyTypeAny && propertyValue != nil) {
|
||||
if ([propertyValue isKindOfClass:[NSString class]]) {
|
||||
// NSStrings are parsed as Any.
|
||||
_type = RLMPropertyTypeString;
|
||||
} else if (![propertyValue isKindOfClass:[RLMListBase class]] && ![propertyValue isKindOfClass:[RLMOptionalBase class]]) {
|
||||
// Don't throw if the property is a List/RealmOptional property because those types only
|
||||
// get reported to ObjC with Swift 1.2 and not 2+.
|
||||
throwForPropertyName(self.name);
|
||||
}
|
||||
} else if (_objcType == 'c') {
|
||||
// Check if it's a BOOL or Int8 by trying to set it to 2 and seeing if
|
||||
// it actually sets it to 1.
|
||||
[obj setValue:@2 forKey:name];
|
||||
NSNumber *value = [obj valueForKey:name];
|
||||
_type = value.intValue == 2 ? RLMPropertyTypeInt : RLMPropertyTypeBool;
|
||||
}
|
||||
|
||||
// update getter/setter names
|
||||
[self updateAccessors];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithName:(NSString *)name
|
||||
indexed:(BOOL)indexed
|
||||
property:(objc_property_t)property
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_name = name;
|
||||
_indexed = indexed;
|
||||
|
||||
if ([self parseObjcProperty:property]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (![self setTypeFromRawType]) {
|
||||
@throw RLMException(@"Can't persist property '%@' with incompatible type. "
|
||||
"Add to ignoredPropertyNames: method to ignore.", self.name);
|
||||
}
|
||||
|
||||
// update getter/setter names
|
||||
[self updateAccessors];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initSwiftListPropertyWithName:(NSString *)name
|
||||
ivar:(Ivar)ivar
|
||||
objectClassName:(NSString *)objectClassName {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_name = name;
|
||||
_type = RLMPropertyTypeArray;
|
||||
_objectClassName = objectClassName;
|
||||
_objcType = 't';
|
||||
_swiftIvar = ivar;
|
||||
|
||||
// no obj-c property for generic lists, and thus no getter/setter names
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initSwiftOptionalPropertyWithName:(NSString *)name
|
||||
indexed:(BOOL)indexed
|
||||
ivar:(Ivar)ivar
|
||||
propertyType:(RLMPropertyType)propertyType {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_name = name;
|
||||
_type = propertyType;
|
||||
_indexed = indexed;
|
||||
_objcType = '@';
|
||||
_swiftIvar = ivar;
|
||||
_optional = true;
|
||||
|
||||
// no obj-c property for generic optionals, and thus no getter/setter names
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone {
|
||||
RLMProperty *prop = [[RLMProperty allocWithZone:zone] init];
|
||||
prop->_name = _name;
|
||||
prop->_type = _type;
|
||||
prop->_objcType = _objcType;
|
||||
prop->_objectClassName = _objectClassName;
|
||||
prop->_indexed = _indexed;
|
||||
prop->_getterName = _getterName;
|
||||
prop->_setterName = _setterName;
|
||||
prop->_getterSel = _getterSel;
|
||||
prop->_setterSel = _setterSel;
|
||||
prop->_isPrimary = _isPrimary;
|
||||
prop->_swiftIvar = _swiftIvar;
|
||||
prop->_optional = _optional;
|
||||
prop->_declarationIndex = _declarationIndex;
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
- (BOOL)isEqualToProperty:(RLMProperty *)property {
|
||||
return _type == property->_type
|
||||
&& _indexed == property->_indexed
|
||||
&& _isPrimary == property->_isPrimary
|
||||
&& _optional == property->_optional
|
||||
&& [_name isEqualToString:property->_name]
|
||||
&& (_objectClassName == property->_objectClassName || [_objectClassName isEqualToString:property->_objectClassName]);
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
return [NSString stringWithFormat:@"%@ {\n\ttype = %@;\n\tobjectClassName = %@;\n\tindexed = %@;\n\tisPrimary = %@;\n\toptional = %@;\n}", self.name, RLMTypeToString(self.type), self.objectClassName, self.indexed ? @"YES" : @"NO", self.isPrimary ? @"YES" : @"NO", self.optional ? @"YES" : @"NO"];
|
||||
}
|
||||
|
||||
@end
|
||||
1237
Example/Pods/Realm/Realm/RLMQueryUtil.mm
generated
Normal file
1237
Example/Pods/Realm/Realm/RLMQueryUtil.mm
generated
Normal file
File diff suppressed because it is too large
Load Diff
783
Example/Pods/Realm/Realm/RLMRealm.mm
generated
Normal file
783
Example/Pods/Realm/Realm/RLMRealm.mm
generated
Normal file
@@ -0,0 +1,783 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RLMAnalytics.hpp"
|
||||
#import "RLMArray_Private.hpp"
|
||||
#import "RLMRealmConfiguration_Private.h"
|
||||
#import "RLMMigration_Private.h"
|
||||
#import "RLMObjectSchema_Private.hpp"
|
||||
#import "RLMProperty_Private.h"
|
||||
#import "RLMObjectStore.h"
|
||||
#import "RLMObject_Private.h"
|
||||
#import "RLMObject_Private.hpp"
|
||||
#import "RLMObservation.hpp"
|
||||
#import "RLMProperty.h"
|
||||
#import "RLMQueryUtil.hpp"
|
||||
#import "RLMRealmUtil.hpp"
|
||||
#import "RLMSchema_Private.hpp"
|
||||
#import "RLMUpdateChecker.hpp"
|
||||
#import "RLMUtil.hpp"
|
||||
|
||||
#include "impl/realm_coordinator.hpp"
|
||||
#include "object_store.hpp"
|
||||
#include "schema.hpp"
|
||||
#include "shared_realm.hpp"
|
||||
|
||||
#include <realm/commit_log.hpp>
|
||||
#include <realm/disable_sync_to_disk.hpp>
|
||||
#include <realm/version.hpp>
|
||||
|
||||
using namespace realm;
|
||||
using util::File;
|
||||
|
||||
@interface RLMRealmConfiguration ()
|
||||
- (realm::Realm::Config&)config;
|
||||
@end
|
||||
|
||||
@interface RLMRealm ()
|
||||
- (void)sendNotifications:(NSString *)notification;
|
||||
@end
|
||||
|
||||
void RLMDisableSyncToDisk() {
|
||||
realm::disable_sync_to_disk();
|
||||
}
|
||||
|
||||
// Notification Token
|
||||
@interface RLMRealmNotificationToken : RLMNotificationToken
|
||||
@property (nonatomic, strong) RLMRealm *realm;
|
||||
@property (nonatomic, copy) RLMNotificationBlock block;
|
||||
@end
|
||||
|
||||
@implementation RLMRealmNotificationToken
|
||||
- (void)stop {
|
||||
[_realm removeNotification:self];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (_realm || _block) {
|
||||
NSLog(@"RLMNotificationToken released without unregistering a notification. You must hold "
|
||||
@"on to the RLMNotificationToken returned from addNotificationBlock and call "
|
||||
@"removeNotification: when you no longer wish to recieve RLMRealm notifications.");
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
static bool shouldForciblyDisableEncryption() {
|
||||
static bool disableEncryption = getenv("REALM_DISABLE_ENCRYPTION");
|
||||
return disableEncryption;
|
||||
}
|
||||
|
||||
NSData *RLMRealmValidatedEncryptionKey(NSData *key) {
|
||||
if (shouldForciblyDisableEncryption()) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (key) {
|
||||
if (key.length != 64) {
|
||||
@throw RLMException(@"Encryption key must be exactly 64 bytes long");
|
||||
}
|
||||
#if TARGET_OS_WATCH
|
||||
@throw RLMException(@"Cannot open an encrypted Realm on watchOS.");
|
||||
#endif
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
@implementation RLMRealm {
|
||||
NSHashTable *_collectionEnumerators;
|
||||
NSHashTable *_notificationHandlers;
|
||||
}
|
||||
|
||||
+ (BOOL)isCoreDebug {
|
||||
return realm::Version::has_feature(realm::feature_Debug);
|
||||
}
|
||||
|
||||
+ (void)initialize {
|
||||
static bool initialized;
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
initialized = true;
|
||||
|
||||
RLMCheckForUpdates();
|
||||
RLMInstallUncaughtExceptionHandler();
|
||||
RLMSendAnalytics();
|
||||
}
|
||||
|
||||
- (BOOL)isEmpty {
|
||||
return realm::ObjectStore::is_empty(self.group);
|
||||
}
|
||||
|
||||
- (void)verifyThread {
|
||||
_realm->verify_thread();
|
||||
}
|
||||
|
||||
- (BOOL)inWriteTransaction {
|
||||
return _realm->is_in_transaction();
|
||||
}
|
||||
|
||||
- (NSString *)path {
|
||||
return @(_realm->config().path.c_str());
|
||||
}
|
||||
|
||||
- (realm::Group *)group {
|
||||
return _realm->read_group();
|
||||
}
|
||||
|
||||
- (BOOL)isReadOnly {
|
||||
return _realm->config().read_only;
|
||||
}
|
||||
|
||||
-(BOOL)autorefresh {
|
||||
return _realm->auto_refresh();
|
||||
}
|
||||
|
||||
- (void)setAutorefresh:(BOOL)autorefresh {
|
||||
_realm->set_auto_refresh(autorefresh);
|
||||
}
|
||||
|
||||
+ (NSString *)writeableTemporaryPathForFile:(NSString *)fileName {
|
||||
return [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
|
||||
}
|
||||
|
||||
+ (instancetype)defaultRealm {
|
||||
return [RLMRealm realmWithConfiguration:[RLMRealmConfiguration rawDefaultConfiguration] error:nil];
|
||||
}
|
||||
|
||||
+ (instancetype)realmWithPath:(NSString *)path {
|
||||
RLMRealmConfiguration *configuration = [RLMRealmConfiguration defaultConfiguration];
|
||||
configuration.path = path;
|
||||
return [RLMRealm realmWithConfiguration:configuration error:nil];
|
||||
}
|
||||
|
||||
+ (instancetype)realmWithPath:(NSString *)path
|
||||
key:(NSData *)key
|
||||
readOnly:(BOOL)readonly
|
||||
inMemory:(BOOL)inMemory
|
||||
dynamic:(BOOL)dynamic
|
||||
schema:(RLMSchema *)customSchema
|
||||
error:(NSError **)outError
|
||||
{
|
||||
RLMRealmConfiguration *configuration = [[RLMRealmConfiguration alloc] init];
|
||||
if (inMemory) {
|
||||
configuration.inMemoryIdentifier = path.lastPathComponent;
|
||||
}
|
||||
else {
|
||||
configuration.path = path;
|
||||
}
|
||||
configuration.encryptionKey = key;
|
||||
configuration.readOnly = readonly;
|
||||
configuration.dynamic = dynamic;
|
||||
configuration.customSchema = customSchema;
|
||||
return [RLMRealm realmWithConfiguration:configuration error:outError];
|
||||
}
|
||||
|
||||
// ARC tries to eliminate calls to autorelease when the value is then immediately
|
||||
// returned, but this results in significantly different semantics between debug
|
||||
// and release builds for RLMRealm, so force it to always autorelease.
|
||||
static id RLMAutorelease(id value) {
|
||||
// +1 __bridge_retained, -1 CFAutorelease
|
||||
return value ? (__bridge id)CFAutorelease((__bridge_retained CFTypeRef)value) : nil;
|
||||
}
|
||||
|
||||
static void RLMCopyColumnMapping(RLMObjectSchema *targetSchema, const ObjectSchema &tableSchema) {
|
||||
REALM_ASSERT_DEBUG(targetSchema.properties.count == tableSchema.properties.size());
|
||||
|
||||
// copy updated column mapping
|
||||
for (auto const& prop : tableSchema.properties) {
|
||||
RLMProperty *targetProp = targetSchema[@(prop.name.c_str())];
|
||||
targetProp.column = prop.table_column;
|
||||
}
|
||||
|
||||
// re-order properties
|
||||
[targetSchema sortPropertiesByColumn];
|
||||
}
|
||||
|
||||
static void RLMRealmSetSchemaAndAlign(RLMRealm *realm, RLMSchema *targetSchema) {
|
||||
realm.schema = targetSchema;
|
||||
for (auto const& aligned : *realm->_realm->config().schema) {
|
||||
if (RLMObjectSchema *objectSchema = [targetSchema schemaForClassName:@(aligned.name.c_str())]) {
|
||||
objectSchema.realm = realm;
|
||||
RLMCopyColumnMapping(objectSchema, aligned);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (instancetype)realmWithSharedRealm:(SharedRealm)sharedRealm schema:(RLMSchema *)schema {
|
||||
RLMRealm *realm = [RLMRealm new];
|
||||
realm->_realm = sharedRealm;
|
||||
realm->_dynamic = YES;
|
||||
RLMRealmSetSchemaAndAlign(realm, schema);
|
||||
return RLMAutorelease(realm);
|
||||
}
|
||||
|
||||
void RLMRealmTranslateException(NSError **error) {
|
||||
try {
|
||||
throw;
|
||||
}
|
||||
catch (RealmFileException const& ex) {
|
||||
switch (ex.kind()) {
|
||||
case RealmFileException::Kind::PermissionDenied:
|
||||
RLMSetErrorOrThrow(RLMMakeError(RLMErrorFilePermissionDenied, ex), error);
|
||||
break;
|
||||
case RealmFileException::Kind::IncompatibleLockFile: {
|
||||
NSString *err = @"Realm file is currently open in another process "
|
||||
"which cannot share access with this process. All "
|
||||
"processes sharing a single file must be the same "
|
||||
"architecture. For sharing files between the Realm "
|
||||
"Browser and an iOS simulator, this means that you "
|
||||
"must use a 64-bit simulator.";
|
||||
RLMSetErrorOrThrow(RLMMakeError(RLMErrorIncompatibleLockFile,
|
||||
File::PermissionDenied(err.UTF8String, ex.path())), error);
|
||||
break;
|
||||
}
|
||||
case RealmFileException::Kind::NotFound:
|
||||
RLMSetErrorOrThrow(RLMMakeError(RLMErrorFileNotFound, ex), error);
|
||||
break;
|
||||
case RealmFileException::Kind::Exists:
|
||||
RLMSetErrorOrThrow(RLMMakeError(RLMErrorFileExists, ex), error);
|
||||
break;
|
||||
case RealmFileException::Kind::AccessError:
|
||||
RLMSetErrorOrThrow(RLMMakeError(RLMErrorFileAccess, ex), error);
|
||||
break;
|
||||
case RealmFileException::Kind::FormatUpgradeRequired:
|
||||
RLMSetErrorOrThrow(RLMMakeError(RLMErrorFileFormatUpgradeRequired, ex), error);
|
||||
break;
|
||||
default:
|
||||
RLMSetErrorOrThrow(RLMMakeError(RLMErrorFail, ex), error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (std::system_error const& ex) {
|
||||
RLMSetErrorOrThrow(RLMMakeError(ex), error);
|
||||
}
|
||||
catch (const std::exception &exp) {
|
||||
RLMSetErrorOrThrow(RLMMakeError(RLMErrorFail, exp), error);
|
||||
}
|
||||
}
|
||||
|
||||
+ (SharedRealm)openSharedRealm:(Realm::Config const&)config error:(NSError **)outError {
|
||||
try {
|
||||
return Realm::get_shared_realm(config);
|
||||
}
|
||||
catch (...) {
|
||||
RLMRealmTranslateException(outError);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
+ (instancetype)realmWithConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error {
|
||||
bool dynamic = configuration.dynamic;
|
||||
bool readOnly = configuration.readOnly;
|
||||
|
||||
{
|
||||
Realm::Config& config = configuration.config;
|
||||
|
||||
// try to reuse existing realm first
|
||||
if (config.cache || dynamic) {
|
||||
if (RLMRealm *realm = RLMGetThreadLocalCachedRealmForPath(config.path)) {
|
||||
auto const& old_config = realm->_realm->config();
|
||||
if (old_config.read_only != config.read_only) {
|
||||
@throw RLMException(@"Realm at path '%s' already opened with different read permissions", config.path.c_str());
|
||||
}
|
||||
if (old_config.in_memory != config.in_memory) {
|
||||
@throw RLMException(@"Realm at path '%s' already opened with different inMemory settings", config.path.c_str());
|
||||
}
|
||||
if (realm->_dynamic != dynamic) {
|
||||
@throw RLMException(@"Realm at path '%s' already opened with different dynamic settings", config.path.c_str());
|
||||
}
|
||||
if (old_config.encryption_key != config.encryption_key) {
|
||||
@throw RLMException(@"Realm at path '%s' already opened with different encryption key", config.path.c_str());
|
||||
}
|
||||
return RLMAutorelease(realm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configuration = [configuration copy];
|
||||
Realm::Config& config = configuration.config;
|
||||
|
||||
RLMRealm *realm = [RLMRealm new];
|
||||
realm->_dynamic = dynamic;
|
||||
|
||||
auto migrationBlock = configuration.migrationBlock;
|
||||
if (migrationBlock && config.schema_version > 0) {
|
||||
auto customSchema = configuration.customSchema;
|
||||
config.migration_function = [=](SharedRealm old_realm, SharedRealm realm) {
|
||||
RLMSchema *oldSchema = [RLMSchema dynamicSchemaFromObjectStoreSchema:*old_realm->config().schema];
|
||||
RLMRealm *oldRealm = [RLMRealm realmWithSharedRealm:old_realm schema:oldSchema];
|
||||
|
||||
// The destination RLMRealm can't just use the schema from the
|
||||
// SharedRealm because it doesn't have information about whether or
|
||||
// not a class was defined in Swift, which effects how new objects
|
||||
// are created
|
||||
RLMSchema *newSchema = [customSchema ?: RLMSchema.sharedSchema copy];
|
||||
RLMRealm *newRealm = [RLMRealm realmWithSharedRealm:realm schema:newSchema];
|
||||
|
||||
[[[RLMMigration alloc] initWithRealm:newRealm oldRealm:oldRealm] execute:migrationBlock];
|
||||
|
||||
oldRealm->_realm = nullptr;
|
||||
newRealm->_realm = nullptr;
|
||||
};
|
||||
}
|
||||
else {
|
||||
config.migration_function = [](SharedRealm, SharedRealm) { };
|
||||
}
|
||||
|
||||
bool beganReadTransaction = false;
|
||||
|
||||
// protects the realm cache and accessors cache
|
||||
static id initLock = [NSObject new];
|
||||
@synchronized(initLock) {
|
||||
realm->_realm = [self openSharedRealm:config error:error];
|
||||
if (!realm->_realm) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// if we have a cached realm on another thread, copy without a transaction
|
||||
if (RLMRealm *cachedRealm = RLMGetAnyCachedRealmForPath(config.path)) {
|
||||
realm.schema = [cachedRealm.schema shallowCopy];
|
||||
for (RLMObjectSchema *objectSchema in realm.schema.objectSchema) {
|
||||
objectSchema.realm = realm;
|
||||
}
|
||||
}
|
||||
else {
|
||||
beganReadTransaction = !realm->_realm->is_in_read_transaction();
|
||||
|
||||
try {
|
||||
// set/align schema or perform migration if needed
|
||||
RLMSchema *schema = [configuration.customSchema copy];
|
||||
if (!schema) {
|
||||
if (dynamic) {
|
||||
schema = [RLMSchema dynamicSchemaFromObjectStoreSchema:*realm->_realm->config().schema];
|
||||
}
|
||||
else {
|
||||
schema = [RLMSchema.sharedSchema copy];
|
||||
realm->_realm->update_schema(schema.objectStoreCopy, config.schema_version);
|
||||
}
|
||||
}
|
||||
|
||||
RLMRealmSetSchemaAndAlign(realm, schema);
|
||||
} catch (std::exception const& exception) {
|
||||
RLMSetErrorOrThrow(RLMMakeError(RLMException(exception)), error);
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!dynamic || configuration.customSchema) {
|
||||
RLMRealmCreateAccessors(realm.schema);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.cache) {
|
||||
RLMCacheRealm(config.path, realm);
|
||||
}
|
||||
}
|
||||
|
||||
if (!readOnly) {
|
||||
// initializing the schema started a read transaction, so end it
|
||||
if (beganReadTransaction) {
|
||||
[realm invalidate];
|
||||
}
|
||||
realm->_realm->m_binding_context = RLMCreateBindingContext(realm);
|
||||
}
|
||||
|
||||
return RLMAutorelease(realm);
|
||||
}
|
||||
|
||||
+ (void)resetRealmState {
|
||||
RLMClearRealmCache();
|
||||
realm::_impl::RealmCoordinator::clear_cache();
|
||||
[RLMRealmConfiguration resetRealmConfigurationState];
|
||||
}
|
||||
|
||||
static void CheckReadWrite(RLMRealm *realm, NSString *msg=@"Cannot write to a read-only Realm") {
|
||||
if (realm.readOnly) {
|
||||
@throw RLMException(@"%@", msg);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)verifyNotificationsAreSupported {
|
||||
[self verifyThread];
|
||||
CheckReadWrite(self, @"Read-only Realms do not change and do not have change notifications");
|
||||
if (!_realm->can_deliver_notifications()) {
|
||||
@throw RLMException(@"Can only add notification blocks from within runloops.");
|
||||
}
|
||||
}
|
||||
|
||||
- (RLMNotificationToken *)addNotificationBlock:(RLMNotificationBlock)block {
|
||||
if (!block) {
|
||||
@throw RLMException(@"The notification block should not be nil");
|
||||
}
|
||||
[self verifyNotificationsAreSupported];
|
||||
|
||||
_realm->read_group();
|
||||
|
||||
if (!_notificationHandlers) {
|
||||
_notificationHandlers = [NSHashTable hashTableWithOptions:NSPointerFunctionsWeakMemory];
|
||||
}
|
||||
|
||||
RLMRealmNotificationToken *token = [[RLMRealmNotificationToken alloc] init];
|
||||
token.realm = self;
|
||||
token.block = block;
|
||||
[_notificationHandlers addObject:token];
|
||||
return token;
|
||||
}
|
||||
|
||||
- (void)removeNotification:(RLMNotificationToken *)token {
|
||||
[self verifyThread];
|
||||
if (auto realmToken = RLMDynamicCast<RLMRealmNotificationToken>(token)) {
|
||||
[_notificationHandlers removeObject:token];
|
||||
realmToken.realm = nil;
|
||||
realmToken.block = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sendNotifications:(NSString *)notification {
|
||||
NSAssert(!self.readOnly, @"Read-only realms do not have notifications");
|
||||
|
||||
// call this realms notification blocks
|
||||
for (RLMRealmNotificationToken *token in [_notificationHandlers allObjects]) {
|
||||
if (token.block) {
|
||||
token.block(notification, self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (RLMRealmConfiguration *)configuration {
|
||||
RLMRealmConfiguration *configuration = [[RLMRealmConfiguration alloc] init];
|
||||
configuration.config = _realm->config();
|
||||
configuration.dynamic = _dynamic;
|
||||
configuration.customSchema = _schema;
|
||||
return configuration;
|
||||
}
|
||||
|
||||
- (void)beginWriteTransaction {
|
||||
try {
|
||||
_realm->begin_transaction();
|
||||
}
|
||||
catch (std::exception &ex) {
|
||||
@throw RLMException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)commitWriteTransaction {
|
||||
[self commitWriteTransaction:nil];
|
||||
}
|
||||
|
||||
- (BOOL)commitWriteTransaction:(NSError **)outError {
|
||||
try {
|
||||
_realm->commit_transaction();
|
||||
return YES;
|
||||
}
|
||||
catch (File::AccessError const& ex) {
|
||||
RLMSetErrorOrThrow(RLMMakeError(RLMErrorFail, ex), outError);
|
||||
return NO;
|
||||
}
|
||||
catch (std::exception const& ex) {
|
||||
RLMSetErrorOrThrow(RLMMakeError(RLMErrorFail, ex), outError);
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)transactionWithBlock:(void(^)(void))block {
|
||||
[self transactionWithBlock:block error:nil];
|
||||
}
|
||||
|
||||
- (BOOL)transactionWithBlock:(void(^)(void))block error:(NSError **)outError {
|
||||
[self beginWriteTransaction];
|
||||
block();
|
||||
if (_realm->is_in_transaction()) {
|
||||
return [self commitWriteTransaction:outError];
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)cancelWriteTransaction {
|
||||
try {
|
||||
_realm->cancel_transaction();
|
||||
}
|
||||
catch (std::exception &ex) {
|
||||
@throw RLMException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)invalidate {
|
||||
if (_realm->is_in_transaction()) {
|
||||
NSLog(@"WARNING: An RLMRealm instance was invalidated during a write "
|
||||
"transaction and all pending changes have been rolled back.");
|
||||
}
|
||||
|
||||
[self detachAllEnumerators];
|
||||
|
||||
for (RLMObjectSchema *objectSchema in _schema.objectSchema) {
|
||||
for (RLMObservationInfo *info : objectSchema->_observedObjects) {
|
||||
info->willChange(RLMInvalidatedKey);
|
||||
}
|
||||
}
|
||||
|
||||
_realm->invalidate();
|
||||
|
||||
for (RLMObjectSchema *objectSchema in _schema.objectSchema) {
|
||||
for (RLMObservationInfo *info : objectSchema->_observedObjects) {
|
||||
info->didChange(RLMInvalidatedKey);
|
||||
}
|
||||
objectSchema.table = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Replaces all string columns in this Realm with a string enumeration column and compacts the
|
||||
database file.
|
||||
|
||||
Cannot be called from a write transaction.
|
||||
|
||||
Compaction will not occur if other `RLMRealm` instances exist.
|
||||
|
||||
While compaction is in progress, attempts by other threads or processes to open the database will
|
||||
wait.
|
||||
|
||||
Be warned that resource requirements for compaction is proportional to the amount of live data in
|
||||
the database.
|
||||
|
||||
Compaction works by writing the database contents to a temporary database file and then replacing
|
||||
the database with the temporary one. The name of the temporary file is formed by appending
|
||||
`.tmp_compaction_space` to the name of the database.
|
||||
|
||||
@return YES if the compaction succeeded.
|
||||
*/
|
||||
- (BOOL)compact {
|
||||
// compact() automatically ends the read transaction, but we need to clean
|
||||
// up cached state and send invalidated notifications when that happens, so
|
||||
// explicitly end it first unless we're in a write transaction (in which
|
||||
// case compact() will throw an exception)
|
||||
if (!_realm->is_in_transaction()) {
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
try {
|
||||
return _realm->compact();
|
||||
}
|
||||
catch (std::exception const& ex) {
|
||||
@throw RLMException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (_realm) {
|
||||
if (_realm->is_in_transaction()) {
|
||||
[self cancelWriteTransaction];
|
||||
NSLog(@"WARNING: An RLMRealm instance was deallocated during a write transaction and all "
|
||||
"pending changes have been rolled back. Make sure to retain a reference to the "
|
||||
"RLMRealm for the duration of the write transaction.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)refresh {
|
||||
return _realm->refresh();
|
||||
}
|
||||
|
||||
- (void)addObject:(__unsafe_unretained RLMObject *const)object {
|
||||
RLMAddObjectToRealm(object, self, false);
|
||||
}
|
||||
|
||||
- (void)addObjects:(id<NSFastEnumeration>)array {
|
||||
for (RLMObject *obj in array) {
|
||||
if (![obj isKindOfClass:[RLMObject class]]) {
|
||||
@throw RLMException(@"Cannot insert objects of type %@ with addObjects:. Only RLMObjects are supported.",
|
||||
NSStringFromClass(obj.class));
|
||||
}
|
||||
[self addObject:obj];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addOrUpdateObject:(RLMObject *)object {
|
||||
// verify primary key
|
||||
if (!object.objectSchema.primaryKeyProperty) {
|
||||
@throw RLMException(@"'%@' does not have a primary key and can not be updated", object.objectSchema.className);
|
||||
}
|
||||
|
||||
RLMAddObjectToRealm(object, self, true);
|
||||
}
|
||||
|
||||
- (void)addOrUpdateObjectsFromArray:(id)array {
|
||||
for (RLMObject *obj in array) {
|
||||
[self addOrUpdateObject:obj];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)deleteObject:(RLMObject *)object {
|
||||
RLMDeleteObjectFromRealm(object, self);
|
||||
}
|
||||
|
||||
- (void)deleteObjects:(id)array {
|
||||
if ([array respondsToSelector:@selector(realm)] && [array respondsToSelector:@selector(deleteObjectsFromRealm)]) {
|
||||
if (self != (RLMRealm *)[array realm]) {
|
||||
@throw RLMException(@"Can only delete objects from the Realm they belong to.");
|
||||
}
|
||||
[array deleteObjectsFromRealm];
|
||||
}
|
||||
else if ([array conformsToProtocol:@protocol(NSFastEnumeration)]) {
|
||||
for (id obj in array) {
|
||||
if ([obj isKindOfClass:RLMObjectBase.class]) {
|
||||
RLMDeleteObjectFromRealm(obj, self);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@throw RLMException(@"Invalid array type - container must be an RLMArray, RLMArray, or NSArray of RLMObjects");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)deleteAllObjects {
|
||||
RLMDeleteAllObjectsFromRealm(self);
|
||||
}
|
||||
|
||||
- (RLMResults *)allObjects:(NSString *)objectClassName {
|
||||
return RLMGetObjects(self, objectClassName, nil);
|
||||
}
|
||||
|
||||
- (RLMResults *)objects:(NSString *)objectClassName where:(NSString *)predicateFormat, ... {
|
||||
va_list args;
|
||||
va_start(args, predicateFormat);
|
||||
RLMResults *results = [self objects:objectClassName where:predicateFormat args:args];
|
||||
va_end(args);
|
||||
return results;
|
||||
}
|
||||
|
||||
- (RLMResults *)objects:(NSString *)objectClassName where:(NSString *)predicateFormat args:(va_list)args {
|
||||
return [self objects:objectClassName withPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]];
|
||||
}
|
||||
|
||||
- (RLMResults *)objects:(NSString *)objectClassName withPredicate:(NSPredicate *)predicate {
|
||||
return RLMGetObjects(self, objectClassName, predicate);
|
||||
}
|
||||
|
||||
- (RLMObject *)objectWithClassName:(NSString *)className forPrimaryKey:(id)primaryKey {
|
||||
return RLMGetObject(self, className, primaryKey);
|
||||
}
|
||||
|
||||
+ (uint64_t)schemaVersionAtPath:(NSString *)realmPath error:(NSError **)error {
|
||||
return [RLMRealm schemaVersionAtPath:realmPath encryptionKey:nil error:error];
|
||||
}
|
||||
|
||||
+ (uint64_t)schemaVersionAtPath:(NSString *)realmPath encryptionKey:(NSData *)key error:(NSError **)outError {
|
||||
try {
|
||||
RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init];
|
||||
config.path = realmPath;
|
||||
config.encryptionKey = RLMRealmValidatedEncryptionKey(key);
|
||||
|
||||
uint64_t version = Realm::get_schema_version(config.config);
|
||||
if (version == realm::ObjectStore::NotVersioned) {
|
||||
RLMSetErrorOrThrow([NSError errorWithDomain:RLMErrorDomain code:RLMErrorFail userInfo:@{NSLocalizedDescriptionKey:@"Cannot open an uninitialized realm in read-only mode"}], outError);
|
||||
}
|
||||
return version;
|
||||
}
|
||||
catch (std::exception &exp) {
|
||||
RLMSetErrorOrThrow(RLMMakeError(RLMErrorFail, exp), outError);
|
||||
return RLMNotVersioned;
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSError *)migrateRealm:(RLMRealmConfiguration *)configuration {
|
||||
if (RLMGetAnyCachedRealmForPath(configuration.config.path)) {
|
||||
@throw RLMException(@"Cannot migrate Realms that are already open.");
|
||||
}
|
||||
|
||||
@autoreleasepool {
|
||||
NSError *error;
|
||||
[RLMRealm realmWithConfiguration:configuration error:&error];
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
- (RLMObject *)createObject:(NSString *)className withValue:(id)value {
|
||||
return (RLMObject *)RLMCreateObjectInRealmWithValue(self, className, value, false);
|
||||
}
|
||||
|
||||
- (BOOL)writeCopyToPath:(NSString *)path key:(NSData *)key error:(NSError **)error {
|
||||
key = RLMRealmValidatedEncryptionKey(key);
|
||||
|
||||
try {
|
||||
self.group->write(path.UTF8String, static_cast<const char *>(key.bytes));
|
||||
return YES;
|
||||
}
|
||||
catch (File::PermissionDenied &ex) {
|
||||
if (error) {
|
||||
*error = RLMMakeError(RLMErrorFilePermissionDenied, ex);
|
||||
}
|
||||
}
|
||||
catch (File::Exists &ex) {
|
||||
if (error) {
|
||||
*error = RLMMakeError(RLMErrorFileExists, ex);
|
||||
}
|
||||
}
|
||||
catch (File::NotFound &ex) {
|
||||
if (error) {
|
||||
*error = RLMMakeError(RLMErrorFileNotFound, ex);
|
||||
}
|
||||
}
|
||||
catch (File::AccessError &ex) {
|
||||
if (error) {
|
||||
*error = RLMMakeError(RLMErrorFileAccess, ex);
|
||||
}
|
||||
}
|
||||
catch (std::exception &ex) {
|
||||
if (error) {
|
||||
*error = RLMMakeError(RLMErrorFail, ex);
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)writeCopyToPath:(NSString *)path error:(NSError **)error {
|
||||
return [self writeCopyToPath:path key:nil error:error];
|
||||
}
|
||||
|
||||
- (BOOL)writeCopyToPath:(NSString *)path encryptionKey:(NSData *)key error:(NSError **)error {
|
||||
if (!key) {
|
||||
@throw RLMException(@"Encryption key must not be nil");
|
||||
}
|
||||
|
||||
return [self writeCopyToPath:path key:key error:error];
|
||||
}
|
||||
|
||||
- (void)registerEnumerator:(RLMFastEnumerator *)enumerator {
|
||||
if (!_collectionEnumerators) {
|
||||
_collectionEnumerators = [NSHashTable hashTableWithOptions:NSPointerFunctionsWeakMemory];
|
||||
}
|
||||
[_collectionEnumerators addObject:enumerator];
|
||||
|
||||
}
|
||||
|
||||
- (void)unregisterEnumerator:(RLMFastEnumerator *)enumerator {
|
||||
[_collectionEnumerators removeObject:enumerator];
|
||||
}
|
||||
|
||||
- (void)detachAllEnumerators {
|
||||
for (RLMFastEnumerator *enumerator in _collectionEnumerators) {
|
||||
[enumerator detach];
|
||||
}
|
||||
_collectionEnumerators = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
262
Example/Pods/Realm/Realm/RLMRealmConfiguration.mm
generated
Normal file
262
Example/Pods/Realm/Realm/RLMRealmConfiguration.mm
generated
Normal file
@@ -0,0 +1,262 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2015 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 "RLMRealmConfiguration_Private.h"
|
||||
|
||||
#import "RLMObjectSchema_Private.hpp"
|
||||
#import "RLMRealm_Private.h"
|
||||
#import "RLMSchema_Private.hpp"
|
||||
#import "RLMUtil.hpp"
|
||||
|
||||
#import "schema.hpp"
|
||||
#import "shared_realm.hpp"
|
||||
|
||||
static NSString *const c_RLMRealmConfigurationProperties[] = {
|
||||
@"path",
|
||||
@"inMemoryIdentifier",
|
||||
@"encryptionKey",
|
||||
@"readOnly",
|
||||
@"schemaVersion",
|
||||
@"migrationBlock",
|
||||
@"dynamic",
|
||||
@"customSchema",
|
||||
};
|
||||
|
||||
static NSString *const c_defaultRealmFileName = @"default.realm";
|
||||
RLMRealmConfiguration *s_defaultConfiguration;
|
||||
|
||||
NSString *RLMRealmPathForFileAndBundleIdentifier(NSString *fileName, NSString *bundleIdentifier) {
|
||||
#if TARGET_OS_TV
|
||||
(void)bundleIdentifier;
|
||||
// tvOS prohibits writing to the Documents directory, so we use the Library/Caches directory instead.
|
||||
NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
|
||||
#elif TARGET_OS_IPHONE
|
||||
(void)bundleIdentifier;
|
||||
// On iOS the Documents directory isn't user-visible, so put files there
|
||||
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
|
||||
#else
|
||||
// On OS X it is, so put files in Application Support. If we aren't running
|
||||
// in a sandbox, put it in a subdirectory based on the bundle identifier
|
||||
// to avoid accidentally sharing files between applications
|
||||
NSString *path = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES)[0];
|
||||
if (![[NSProcessInfo processInfo] environment][@"APP_SANDBOX_CONTAINER_ID"]) {
|
||||
if (!bundleIdentifier) {
|
||||
bundleIdentifier = [NSBundle mainBundle].bundleIdentifier;
|
||||
}
|
||||
if (!bundleIdentifier) {
|
||||
bundleIdentifier = [NSBundle mainBundle].executablePath.lastPathComponent;
|
||||
}
|
||||
|
||||
path = [path stringByAppendingPathComponent:bundleIdentifier];
|
||||
|
||||
// create directory
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:path
|
||||
withIntermediateDirectories:YES
|
||||
attributes:nil
|
||||
error:nil];
|
||||
}
|
||||
#endif
|
||||
return [path stringByAppendingPathComponent:fileName];
|
||||
}
|
||||
|
||||
NSString *RLMRealmPathForFile(NSString *fileName) {
|
||||
return RLMRealmPathForFileAndBundleIdentifier(fileName, nil);
|
||||
}
|
||||
|
||||
@implementation RLMRealmConfiguration {
|
||||
realm::Realm::Config _config;
|
||||
}
|
||||
|
||||
- (realm::Realm::Config&)config {
|
||||
return _config;
|
||||
}
|
||||
|
||||
+ (instancetype)defaultConfiguration {
|
||||
return [[self rawDefaultConfiguration] copy];
|
||||
}
|
||||
|
||||
+ (void)setDefaultConfiguration:(RLMRealmConfiguration *)configuration {
|
||||
if (!configuration) {
|
||||
@throw RLMException(@"Cannot set the default configuration to nil.");
|
||||
}
|
||||
@synchronized(c_defaultRealmFileName) {
|
||||
s_defaultConfiguration = [configuration copy];
|
||||
}
|
||||
}
|
||||
|
||||
+ (RLMRealmConfiguration *)rawDefaultConfiguration {
|
||||
@synchronized(c_defaultRealmFileName) {
|
||||
if (!s_defaultConfiguration) {
|
||||
s_defaultConfiguration = [[RLMRealmConfiguration alloc] init];
|
||||
}
|
||||
}
|
||||
return s_defaultConfiguration;
|
||||
}
|
||||
|
||||
+ (void)resetRealmConfigurationState {
|
||||
@synchronized(c_defaultRealmFileName) {
|
||||
s_defaultConfiguration = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
static NSString *defaultRealmPath = RLMRealmPathForFile(c_defaultRealmFileName);
|
||||
self.path = defaultRealmPath;
|
||||
self.schemaVersion = 0;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
RLMRealmConfiguration *configuration = [[[self class] allocWithZone:zone] init];
|
||||
configuration->_config = _config;
|
||||
configuration->_dynamic = _dynamic;
|
||||
configuration->_migrationBlock = _migrationBlock;
|
||||
configuration->_customSchema = _customSchema;
|
||||
return configuration;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
NSMutableString *string = [NSMutableString stringWithFormat:@"%@ {\n", self.class];
|
||||
for (NSString *key : c_RLMRealmConfigurationProperties) {
|
||||
NSString *description = [[self valueForKey:key] description];
|
||||
description = [description stringByReplacingOccurrencesOfString:@"\n" withString:@"\n\t"];
|
||||
|
||||
[string appendFormat:@"\t%@ = %@;\n", key, description];
|
||||
}
|
||||
return [string stringByAppendingString:@"}"];
|
||||
}
|
||||
|
||||
- (NSString *)path {
|
||||
return _config.in_memory ? nil :@(_config.path.c_str());
|
||||
}
|
||||
|
||||
static void RLMNSStringToStdString(std::string &out, NSString *in) {
|
||||
out.resize([in maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
|
||||
if (out.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSUInteger size = out.size();
|
||||
[in getBytes:&out[0]
|
||||
maxLength:size
|
||||
usedLength:&size
|
||||
encoding:NSUTF8StringEncoding
|
||||
options:0 range:{0, in.length} remainingRange:nullptr];
|
||||
out.resize(size);
|
||||
}
|
||||
|
||||
- (void)setPath:(NSString *)path {
|
||||
if (path.length == 0) {
|
||||
@throw RLMException(@"Realm path must not be empty");
|
||||
}
|
||||
|
||||
RLMNSStringToStdString(_config.path, path);
|
||||
_config.in_memory = false;
|
||||
}
|
||||
|
||||
- (NSString *)inMemoryIdentifier {
|
||||
if (!_config.in_memory) {
|
||||
return nil;
|
||||
}
|
||||
return [@(_config.path.c_str()) lastPathComponent];
|
||||
}
|
||||
|
||||
- (void)setInMemoryIdentifier:(NSString *)inMemoryIdentifier {
|
||||
if (inMemoryIdentifier.length == 0) {
|
||||
@throw RLMException(@"In-memory identifier must not be empty");
|
||||
}
|
||||
|
||||
RLMNSStringToStdString(_config.path, [NSTemporaryDirectory() stringByAppendingPathComponent:inMemoryIdentifier]);
|
||||
_config.in_memory = true;
|
||||
}
|
||||
|
||||
- (NSData *)encryptionKey {
|
||||
return _config.encryption_key.empty() ? nil : [NSData dataWithBytes:_config.encryption_key.data() length:_config.encryption_key.size()];
|
||||
}
|
||||
|
||||
- (void)setEncryptionKey:(NSData * __nullable)encryptionKey {
|
||||
if (NSData *key = RLMRealmValidatedEncryptionKey(encryptionKey)) {
|
||||
auto bytes = static_cast<const char *>(key.bytes);
|
||||
_config.encryption_key.assign(bytes, bytes + key.length);
|
||||
}
|
||||
else {
|
||||
_config.encryption_key.clear();
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)readOnly {
|
||||
return _config.read_only;
|
||||
}
|
||||
|
||||
- (void)setReadOnly:(BOOL)readOnly {
|
||||
_config.read_only = readOnly;
|
||||
}
|
||||
|
||||
- (uint64_t)schemaVersion {
|
||||
return _config.schema_version;
|
||||
}
|
||||
|
||||
- (void)setSchemaVersion:(uint64_t)schemaVersion {
|
||||
if (schemaVersion == RLMNotVersioned) {
|
||||
@throw RLMException(@"Cannot set schema version to %llu (RLMNotVersioned)", RLMNotVersioned);
|
||||
}
|
||||
_config.schema_version = schemaVersion;
|
||||
}
|
||||
|
||||
- (NSArray *)objectClasses {
|
||||
return [_customSchema.objectSchema valueForKeyPath:@"objectClass"];
|
||||
}
|
||||
|
||||
- (void)setObjectClasses:(NSArray *)objectClasses {
|
||||
self.customSchema = [RLMSchema schemaWithObjectClasses:objectClasses];
|
||||
}
|
||||
|
||||
- (void)setDynamic:(bool)dynamic {
|
||||
_dynamic = dynamic;
|
||||
_config.cache = !dynamic;
|
||||
}
|
||||
|
||||
- (bool)cache {
|
||||
return _config.cache;
|
||||
}
|
||||
|
||||
- (void)setCache:(bool)cache {
|
||||
_config.cache = cache;
|
||||
}
|
||||
|
||||
- (void)setCustomSchema:(RLMSchema *)customSchema {
|
||||
_customSchema = customSchema;
|
||||
_config.schema = [_customSchema objectStoreCopy];
|
||||
}
|
||||
|
||||
- (void)setDisableFormatUpgrade:(bool)disableFormatUpgrade
|
||||
{
|
||||
_config.disable_format_upgrade = disableFormatUpgrade;
|
||||
}
|
||||
|
||||
- (bool)disableFormatUpgrade
|
||||
{
|
||||
return _config.disable_format_upgrade;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
160
Example/Pods/Realm/Realm/RLMRealmUtil.mm
generated
Normal file
160
Example/Pods/Realm/Realm/RLMRealmUtil.mm
generated
Normal file
@@ -0,0 +1,160 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RLMRealmUtil.hpp"
|
||||
|
||||
#import "RLMObservation.hpp"
|
||||
#import "RLMRealm_Private.h"
|
||||
#import "RLMUtil.hpp"
|
||||
|
||||
#import <Realm/RLMConstants.h>
|
||||
#import <Realm/RLMSchema.h>
|
||||
|
||||
#import "binding_context.hpp"
|
||||
|
||||
#import <map>
|
||||
#import <mutex>
|
||||
#import <sys/event.h>
|
||||
#import <sys/stat.h>
|
||||
#import <sys/time.h>
|
||||
#import <unistd.h>
|
||||
|
||||
// Global realm state
|
||||
static std::mutex s_realmCacheMutex;
|
||||
static std::map<std::string, NSMapTable *> s_realmsPerPath;
|
||||
|
||||
void RLMCacheRealm(std::string const& path, RLMRealm *realm) {
|
||||
std::lock_guard<std::mutex> lock(s_realmCacheMutex);
|
||||
NSMapTable *realms = s_realmsPerPath[path];
|
||||
if (!realms) {
|
||||
s_realmsPerPath[path] = realms = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsObjectPersonality
|
||||
valueOptions:NSPointerFunctionsWeakMemory];
|
||||
}
|
||||
[realms setObject:realm forKey:@(pthread_mach_thread_np(pthread_self()))];
|
||||
}
|
||||
|
||||
RLMRealm *RLMGetAnyCachedRealmForPath(std::string const& path) {
|
||||
std::lock_guard<std::mutex> lock(s_realmCacheMutex);
|
||||
return [s_realmsPerPath[path] objectEnumerator].nextObject;
|
||||
}
|
||||
|
||||
RLMRealm *RLMGetThreadLocalCachedRealmForPath(std::string const& path) {
|
||||
mach_port_t threadID = pthread_mach_thread_np(pthread_self());
|
||||
std::lock_guard<std::mutex> lock(s_realmCacheMutex);
|
||||
return [s_realmsPerPath[path] objectForKey:@(threadID)];
|
||||
}
|
||||
|
||||
void RLMClearRealmCache() {
|
||||
std::lock_guard<std::mutex> lock(s_realmCacheMutex);
|
||||
s_realmsPerPath.clear();
|
||||
}
|
||||
|
||||
void RLMInstallUncaughtExceptionHandler() {
|
||||
static auto previousHandler = NSGetUncaughtExceptionHandler();
|
||||
|
||||
NSSetUncaughtExceptionHandler([](NSException *exception) {
|
||||
NSNumber *threadID = @(pthread_mach_thread_np(pthread_self()));
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(s_realmCacheMutex);
|
||||
for (auto const& realmsPerThread : s_realmsPerPath) {
|
||||
if (RLMRealm *realm = [realmsPerThread.second objectForKey:threadID]) {
|
||||
if (realm.inWriteTransaction) {
|
||||
[realm cancelWriteTransaction];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (previousHandler) {
|
||||
previousHandler(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
namespace {
|
||||
class RLMNotificationHelper : public realm::BindingContext {
|
||||
public:
|
||||
RLMNotificationHelper(RLMRealm *realm) : _realm(realm) { }
|
||||
|
||||
bool can_deliver_notifications() const noexcept override {
|
||||
// The main thread may not be in a run loop yet if we're called from
|
||||
// something like `applicationDidFinishLaunching:`, but it presumably will
|
||||
// be in the future
|
||||
if ([NSThread isMainThread]) {
|
||||
return true;
|
||||
}
|
||||
// Current mode indicates why the current callout from the runloop was made,
|
||||
// and is null if a runloop callout isn't currently being processed
|
||||
if (auto mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent())) {
|
||||
CFRelease(mode);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void changes_available() override {
|
||||
@autoreleasepool {
|
||||
auto realm = _realm;
|
||||
if (realm && !realm.autorefresh) {
|
||||
[realm sendNotifications:RLMRealmRefreshRequiredNotification];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ObserverState> get_observed_rows() override {
|
||||
@autoreleasepool {
|
||||
auto realm = _realm;
|
||||
[realm detachAllEnumerators];
|
||||
return RLMGetObservedRows(realm.schema.objectSchema);
|
||||
}
|
||||
}
|
||||
|
||||
void will_change(std::vector<ObserverState> const& observed, std::vector<void*> const& invalidated) override {
|
||||
@autoreleasepool {
|
||||
RLMWillChange(observed, invalidated);
|
||||
}
|
||||
}
|
||||
|
||||
void did_change(std::vector<ObserverState> const& observed, std::vector<void*> const& invalidated) override {
|
||||
try {
|
||||
@autoreleasepool {
|
||||
RLMDidChange(observed, invalidated);
|
||||
[_realm sendNotifications:RLMRealmDidChangeNotification];
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
// This can only be called during a write transaction if it was
|
||||
// called due to the transaction beginning, so cancel it to ensure
|
||||
// exceptions thrown here behave the same as exceptions thrown when
|
||||
// actually beginning the write
|
||||
if (_realm.inWriteTransaction) {
|
||||
[_realm cancelWriteTransaction];
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// This is owned by the realm, so it needs to not retain the realm
|
||||
__weak RLMRealm *const _realm;
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
std::unique_ptr<realm::BindingContext> RLMCreateBindingContext(RLMRealm *realm) {
|
||||
return std::unique_ptr<realm::BindingContext>(new RLMNotificationHelper(realm));
|
||||
}
|
||||
570
Example/Pods/Realm/Realm/RLMResults.mm
generated
Normal file
570
Example/Pods/Realm/Realm/RLMResults.mm
generated
Normal file
@@ -0,0 +1,570 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RLMResults_Private.h"
|
||||
|
||||
#import "RLMArray_Private.hpp"
|
||||
#import "RLMObjectSchema_Private.hpp"
|
||||
#import "RLMObjectStore.h"
|
||||
#import "RLMObject_Private.hpp"
|
||||
#import "RLMObservation.hpp"
|
||||
#import "RLMProperty_Private.h"
|
||||
#import "RLMQueryUtil.hpp"
|
||||
#import "RLMRealm_Private.hpp"
|
||||
#import "RLMSchema_Private.h"
|
||||
#import "RLMUtil.hpp"
|
||||
|
||||
#import "results.hpp"
|
||||
#import "impl/external_commit_helper.hpp"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
#import <objc/message.h>
|
||||
#import <realm/table_view.hpp>
|
||||
|
||||
using namespace realm;
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wincomplete-implementation"
|
||||
@implementation RLMNotificationToken
|
||||
@end
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
@interface RLMCancellationToken : RLMNotificationToken
|
||||
@end
|
||||
|
||||
@implementation RLMCancellationToken {
|
||||
realm::AsyncQueryCancelationToken _token;
|
||||
}
|
||||
- (instancetype)initWithToken:(realm::AsyncQueryCancelationToken)token {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_token = std::move(token);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)stop {
|
||||
_token = {};
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static const int RLMEnumerationBufferSize = 16;
|
||||
|
||||
@implementation RLMFastEnumerator {
|
||||
// The buffer supplied by fast enumeration does not retain the objects given
|
||||
// to it, but because we create objects on-demand and don't want them
|
||||
// autoreleased (a table can have more rows than the device has memory for
|
||||
// accessor objects) we need a thing to retain them.
|
||||
id _strongBuffer[RLMEnumerationBufferSize];
|
||||
|
||||
RLMRealm *_realm;
|
||||
RLMObjectSchema *_objectSchema;
|
||||
|
||||
// Collection being enumerated. Only one of these two will be valid: when
|
||||
// possible we enumerate the collection directly, but when in a write
|
||||
// transaction we instead create a frozen TableView and enumerate that
|
||||
// instead so that mutating the collection during enumeration works.
|
||||
id<RLMFastEnumerable> _collection;
|
||||
realm::TableView _tableView;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCollection:(id<RLMFastEnumerable>)collection objectSchema:(RLMObjectSchema *)objectSchema {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_realm = collection.realm;
|
||||
_objectSchema = objectSchema;
|
||||
|
||||
if (_realm.inWriteTransaction) {
|
||||
_tableView = [collection tableView];
|
||||
}
|
||||
else {
|
||||
_collection = collection;
|
||||
[_realm registerEnumerator:self];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (_collection) {
|
||||
[_realm unregisterEnumerator:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)detach {
|
||||
_tableView = [_collection tableView];
|
||||
_collection = nil;
|
||||
}
|
||||
|
||||
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
|
||||
count:(NSUInteger)len {
|
||||
[_realm verifyThread];
|
||||
if (!_tableView.is_attached() && !_collection) {
|
||||
@throw RLMException(@"Collection is no longer valid");
|
||||
}
|
||||
// The fast enumeration buffer size is currently a hardcoded number in the
|
||||
// compiler so this can't actually happen, but just in case it changes in
|
||||
// the future...
|
||||
if (len > RLMEnumerationBufferSize) {
|
||||
len = RLMEnumerationBufferSize;
|
||||
}
|
||||
|
||||
NSUInteger batchCount = 0, count = state->extra[1];
|
||||
|
||||
Class accessorClass = _objectSchema.accessorClass;
|
||||
for (NSUInteger index = state->state; index < count && batchCount < len; ++index) {
|
||||
RLMObject *accessor = [[accessorClass alloc] initWithRealm:_realm schema:_objectSchema];
|
||||
if (_collection) {
|
||||
accessor->_row = (*_objectSchema.table)[[_collection indexInSource:index]];
|
||||
}
|
||||
else if (_tableView.is_row_attached(index)) {
|
||||
accessor->_row = (*_objectSchema.table)[_tableView.get_source_ndx(index)];
|
||||
}
|
||||
RLMInitializeSwiftAccessorGenerics(accessor);
|
||||
_strongBuffer[batchCount] = accessor;
|
||||
batchCount++;
|
||||
}
|
||||
|
||||
for (NSUInteger i = batchCount; i < len; ++i) {
|
||||
_strongBuffer[i] = nil;
|
||||
}
|
||||
|
||||
if (batchCount == 0) {
|
||||
// Release our data if we're done, as we're autoreleased and so may
|
||||
// stick around for a while
|
||||
_collection = nil;
|
||||
if (_tableView.is_attached()) {
|
||||
_tableView = TableView();
|
||||
}
|
||||
else {
|
||||
[_realm unregisterEnumerator:self];
|
||||
}
|
||||
}
|
||||
|
||||
state->itemsPtr = (__unsafe_unretained id *)(void *)_strongBuffer;
|
||||
state->state += batchCount;
|
||||
state->mutationsPtr = state->extra+1;
|
||||
|
||||
return batchCount;
|
||||
}
|
||||
@end
|
||||
|
||||
//
|
||||
// RLMResults implementation
|
||||
//
|
||||
@implementation RLMResults {
|
||||
realm::Results _results;
|
||||
RLMRealm *_realm;
|
||||
}
|
||||
|
||||
- (instancetype)initPrivate {
|
||||
self = [super init];
|
||||
return self;
|
||||
}
|
||||
|
||||
static void assertKeyPathIsNotNested(NSString *keyPath) {
|
||||
if ([keyPath rangeOfString:@"."].location != NSNotFound) {
|
||||
@throw RLMException(@"Nested key paths are not supported yet for KVC collection operators.");
|
||||
}
|
||||
}
|
||||
|
||||
[[gnu::noinline]]
|
||||
[[noreturn]]
|
||||
static void throwError(NSString *aggregateMethod) {
|
||||
try {
|
||||
throw;
|
||||
}
|
||||
catch (realm::InvalidTransactionException const&) {
|
||||
@throw RLMException(@"Cannot modify Results outside of a write transaction");
|
||||
}
|
||||
catch (realm::IncorrectThreadException const&) {
|
||||
@throw RLMException(@"Realm accessed from incorrect thread");
|
||||
}
|
||||
catch (realm::Results::InvalidatedException const&) {
|
||||
@throw RLMException(@"RLMResults has been invalidated");
|
||||
}
|
||||
catch (realm::Results::DetatchedAccessorException const&) {
|
||||
@throw RLMException(@"Object has been invalidated");
|
||||
}
|
||||
catch (realm::Results::IncorrectTableException const& e) {
|
||||
@throw RLMException(@"Object type '%s' does not match RLMResults type '%s'.",
|
||||
e.actual.data(), e.expected.data());
|
||||
}
|
||||
catch (realm::Results::OutOfBoundsIndexException const& e) {
|
||||
@throw RLMException(@"Index %zu is out of bounds (must be less than %zu)",
|
||||
e.requested, e.valid_count);
|
||||
}
|
||||
catch (realm::Results::UnsupportedColumnTypeException const& e) {
|
||||
@throw RLMException(@"%@ is not supported for %@ property '%s'",
|
||||
aggregateMethod,
|
||||
RLMTypeToString((RLMPropertyType)e.column_type),
|
||||
e.column_name.data());
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Function>
|
||||
static auto translateErrors(Function&& f, NSString *aggregateMethod=nil) {
|
||||
try {
|
||||
return f();
|
||||
}
|
||||
catch (...) {
|
||||
throwError(aggregateMethod);
|
||||
}
|
||||
}
|
||||
|
||||
+ (instancetype)resultsWithObjectSchema:(RLMObjectSchema *)objectSchema
|
||||
results:(realm::Results)results {
|
||||
RLMResults *ar = [[self alloc] initPrivate];
|
||||
ar->_results = std::move(results);
|
||||
ar->_realm = objectSchema.realm;
|
||||
ar->_objectSchema = objectSchema;
|
||||
return ar;
|
||||
}
|
||||
|
||||
static inline void RLMResultsValidateInWriteTransaction(__unsafe_unretained RLMResults *const ar) {
|
||||
ar->_realm->_realm->verify_thread();
|
||||
ar->_realm->_realm->verify_in_write();
|
||||
}
|
||||
|
||||
- (NSUInteger)count {
|
||||
return translateErrors([&] { return _results.size(); });
|
||||
}
|
||||
|
||||
- (NSString *)objectClassName {
|
||||
return RLMStringDataToNSString(_results.get_object_type());
|
||||
}
|
||||
|
||||
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
|
||||
objects:(__unused __unsafe_unretained id [])buffer
|
||||
count:(NSUInteger)len {
|
||||
__autoreleasing RLMFastEnumerator *enumerator;
|
||||
if (state->state == 0) {
|
||||
enumerator = [[RLMFastEnumerator alloc] initWithCollection:self objectSchema:_objectSchema];
|
||||
state->extra[0] = (long)enumerator;
|
||||
state->extra[1] = self.count;
|
||||
}
|
||||
else {
|
||||
enumerator = (__bridge id)(void *)state->extra[0];
|
||||
}
|
||||
|
||||
return [enumerator countByEnumeratingWithState:state count:len];
|
||||
}
|
||||
|
||||
- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat, ... {
|
||||
va_list args;
|
||||
va_start(args, predicateFormat);
|
||||
NSUInteger index = [self indexOfObjectWhere:predicateFormat args:args];
|
||||
va_end(args);
|
||||
return index;
|
||||
}
|
||||
|
||||
- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat args:(va_list)args {
|
||||
return [self indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:predicateFormat
|
||||
arguments:args]];
|
||||
}
|
||||
|
||||
- (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate {
|
||||
if (_results.get_mode() == Results::Mode::Empty) {
|
||||
return NSNotFound;
|
||||
}
|
||||
|
||||
Query query = translateErrors([&] { return _results.get_query(); });
|
||||
RLMUpdateQueryWithPredicate(&query, predicate, _realm.schema, _objectSchema);
|
||||
|
||||
TableView table_view;
|
||||
if (const auto& sort = _results.get_sort()) {
|
||||
// A sort order is specified so we need to return the first match given that ordering.
|
||||
table_view = query.find_all();
|
||||
table_view.sort(sort.columnIndices, sort.ascending);
|
||||
} else {
|
||||
// No sort order is specified so we only need to find a single match.
|
||||
// FIXME: We're only looking for a single object so we'd like to be able to use `Query::find`
|
||||
// for this, but as of core v0.97.1 it gives incorrect results if the query is restricted
|
||||
// to a link view (<https://github.com/realm/realm-core/issues/1565>).
|
||||
table_view = query.find_all(0, -1, 1);
|
||||
}
|
||||
if (!table_view.size()) {
|
||||
return NSNotFound;
|
||||
}
|
||||
return _results.index_of(table_view.get_source_ndx(0));
|
||||
}
|
||||
|
||||
- (id)objectAtIndex:(NSUInteger)index {
|
||||
return translateErrors([&] {
|
||||
return RLMCreateObjectAccessor(_realm, _objectSchema, _results.get(index));
|
||||
});
|
||||
}
|
||||
|
||||
- (id)firstObject {
|
||||
auto row = translateErrors([&] { return _results.first(); });
|
||||
return row ? RLMCreateObjectAccessor(_realm, _objectSchema, *row) : nil;
|
||||
}
|
||||
|
||||
- (id)lastObject {
|
||||
auto row = translateErrors([&] { return _results.last(); });
|
||||
return row ? RLMCreateObjectAccessor(_realm, _objectSchema, *row) : nil;
|
||||
}
|
||||
|
||||
- (NSUInteger)indexOfObject:(RLMObject *)object {
|
||||
if (!object || (!object->_realm && !object.invalidated)) {
|
||||
return NSNotFound;
|
||||
}
|
||||
|
||||
return translateErrors([&] {
|
||||
return RLMConvertNotFound(_results.index_of(object->_row));
|
||||
});
|
||||
}
|
||||
|
||||
- (id)valueForKeyPath:(NSString *)keyPath {
|
||||
if ([keyPath characterAtIndex:0] == '@') {
|
||||
if ([keyPath isEqualToString:@"@count"]) {
|
||||
return @(self.count);
|
||||
}
|
||||
NSRange operatorRange = [keyPath rangeOfString:@"." options:NSLiteralSearch];
|
||||
NSUInteger keyPathLength = keyPath.length;
|
||||
NSUInteger separatorIndex = operatorRange.location != NSNotFound ? operatorRange.location : keyPathLength;
|
||||
NSString *operatorName = [keyPath substringWithRange:NSMakeRange(1, separatorIndex - 1)];
|
||||
SEL opSelector = NSSelectorFromString([NSString stringWithFormat:@"_%@ForKeyPath:", operatorName]);
|
||||
BOOL isValidOperator = [self respondsToSelector:opSelector];
|
||||
if (!isValidOperator) {
|
||||
@throw RLMException(@"Unsupported KVC collection operator found in key path '%@'", keyPath);
|
||||
}
|
||||
else if (separatorIndex >= keyPathLength - 1) {
|
||||
@throw RLMException(@"Missing key path for KVC collection operator %@ in key path '%@'", operatorName, keyPath);
|
||||
}
|
||||
NSString *operatorKeyPath = [keyPath substringFromIndex:separatorIndex + 1];
|
||||
if (isValidOperator) {
|
||||
return ((id(*)(id, SEL, id))objc_msgSend)(self, opSelector, operatorKeyPath);
|
||||
}
|
||||
}
|
||||
return [super valueForKeyPath:keyPath];
|
||||
}
|
||||
|
||||
- (id)valueForKey:(NSString *)key {
|
||||
return translateErrors([&] {
|
||||
return RLMCollectionValueForKey(self, key);
|
||||
});
|
||||
}
|
||||
|
||||
- (void)setValue:(id)value forKey:(NSString *)key {
|
||||
translateErrors([&] { RLMResultsValidateInWriteTransaction(self); });
|
||||
RLMCollectionSetValueForKey(self, key, value);
|
||||
}
|
||||
|
||||
- (NSNumber *)_aggregateForKeyPath:(NSString *)keyPath method:(util::Optional<Mixed> (Results::*)(size_t))method methodName:(NSString *)methodName {
|
||||
assertKeyPathIsNotNested(keyPath);
|
||||
return [self aggregate:keyPath method:method methodName:methodName];
|
||||
}
|
||||
|
||||
- (NSNumber *)_minForKeyPath:(NSString *)keyPath {
|
||||
return [self _aggregateForKeyPath:keyPath method:&Results::min methodName:@"@min"];
|
||||
}
|
||||
|
||||
- (NSNumber *)_maxForKeyPath:(NSString *)keyPath {
|
||||
return [self _aggregateForKeyPath:keyPath method:&Results::max methodName:@"@max"];
|
||||
}
|
||||
|
||||
- (NSNumber *)_sumForKeyPath:(NSString *)keyPath {
|
||||
return [self _aggregateForKeyPath:keyPath method:&Results::sum methodName:@"@sum"];
|
||||
}
|
||||
|
||||
- (NSNumber *)_avgForKeyPath:(NSString *)keyPath {
|
||||
return [self _aggregateForKeyPath:keyPath method:&Results::average methodName:@"@avg"];
|
||||
}
|
||||
|
||||
- (NSArray *)_unionOfObjectsForKeyPath:(NSString *)keyPath {
|
||||
assertKeyPathIsNotNested(keyPath);
|
||||
return translateErrors([&] {
|
||||
return RLMCollectionValueForKey(self, keyPath);
|
||||
});
|
||||
}
|
||||
|
||||
- (NSArray *)_distinctUnionOfObjectsForKeyPath:(NSString *)keyPath {
|
||||
return [NSSet setWithArray:[self _unionOfObjectsForKeyPath:keyPath]].allObjects;
|
||||
}
|
||||
|
||||
- (NSArray *)_unionOfArraysForKeyPath:(NSString *)keyPath {
|
||||
assertKeyPathIsNotNested(keyPath);
|
||||
if ([keyPath isEqualToString:@"self"]) {
|
||||
@throw RLMException(@"self is not a valid key-path for a KVC array collection operator as 'unionOfArrays'.");
|
||||
}
|
||||
|
||||
return translateErrors([&] {
|
||||
NSArray *nestedResults = RLMCollectionValueForKey(self, keyPath);
|
||||
NSMutableArray *flatArray = [NSMutableArray arrayWithCapacity:nestedResults.count];
|
||||
for (id<RLMFastEnumerable> array in nestedResults) {
|
||||
NSArray *nsArray = RLMCollectionValueForKey(array, @"self");
|
||||
[flatArray addObjectsFromArray:nsArray];
|
||||
}
|
||||
return flatArray;
|
||||
});
|
||||
}
|
||||
|
||||
- (NSArray *)_distinctUnionOfArraysForKeyPath:(__unused NSString *)keyPath {
|
||||
return [NSSet setWithArray:[self _unionOfArraysForKeyPath:keyPath]].allObjects;
|
||||
}
|
||||
|
||||
- (RLMResults *)objectsWhere:(NSString *)predicateFormat, ... {
|
||||
va_list args;
|
||||
va_start(args, predicateFormat);
|
||||
RLMResults *results = [self objectsWhere:predicateFormat args:args];
|
||||
va_end(args);
|
||||
return results;
|
||||
}
|
||||
|
||||
- (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args {
|
||||
return [self objectsWithPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]];
|
||||
}
|
||||
|
||||
- (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate {
|
||||
return translateErrors([&] {
|
||||
if (_results.get_mode() == Results::Mode::Empty) {
|
||||
return self;
|
||||
}
|
||||
auto query = _results.get_query();
|
||||
RLMUpdateQueryWithPredicate(&query, predicate, _realm.schema, _objectSchema);
|
||||
return [RLMResults resultsWithObjectSchema:_objectSchema
|
||||
results:realm::Results(_realm->_realm, std::move(query), _results.get_sort())];
|
||||
});
|
||||
}
|
||||
|
||||
- (RLMResults *)sortedResultsUsingProperty:(NSString *)property ascending:(BOOL)ascending {
|
||||
return [self sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithProperty:property ascending:ascending]]];
|
||||
}
|
||||
|
||||
- (RLMResults *)sortedResultsUsingDescriptors:(NSArray *)properties {
|
||||
return translateErrors([&] {
|
||||
if (_results.get_mode() == Results::Mode::Empty) {
|
||||
return self;
|
||||
}
|
||||
|
||||
return [RLMResults resultsWithObjectSchema:_objectSchema
|
||||
results:_results.sort(RLMSortOrderFromDescriptors(_objectSchema, properties))];
|
||||
});
|
||||
}
|
||||
|
||||
- (id)objectAtIndexedSubscript:(NSUInteger)index {
|
||||
return [self objectAtIndex:index];
|
||||
}
|
||||
|
||||
- (id)aggregate:(NSString *)property method:(util::Optional<Mixed> (Results::*)(size_t))method methodName:(NSString *)methodName {
|
||||
size_t column = RLMValidatedProperty(_objectSchema, property).column;
|
||||
auto value = translateErrors([&] { return (_results.*method)(column); }, methodName);
|
||||
if (!value) {
|
||||
return nil;
|
||||
}
|
||||
return RLMMixedToObjc(*value);
|
||||
}
|
||||
|
||||
- (id)minOfProperty:(NSString *)property {
|
||||
return [self aggregate:property method:&Results::min methodName:@"minOfProperty"];
|
||||
}
|
||||
|
||||
- (id)maxOfProperty:(NSString *)property {
|
||||
return [self aggregate:property method:&Results::max methodName:@"maxOfProperty"];
|
||||
}
|
||||
|
||||
- (id)sumOfProperty:(NSString *)property {
|
||||
return [self aggregate:property method:&Results::sum methodName:@"sumOfProperty"];
|
||||
}
|
||||
|
||||
- (id)averageOfProperty:(NSString *)property {
|
||||
return [self aggregate:property method:&Results::average methodName:@"averageOfProperty"];
|
||||
}
|
||||
|
||||
- (void)deleteObjectsFromRealm {
|
||||
return translateErrors([&] {
|
||||
if (_results.get_mode() == Results::Mode::Table) {
|
||||
RLMResultsValidateInWriteTransaction(self);
|
||||
RLMClearTable(self.objectSchema);
|
||||
}
|
||||
else {
|
||||
RLMTrackDeletions(_realm, ^{ _results.clear(); });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
const NSUInteger maxObjects = 100;
|
||||
NSMutableString *mString = [NSMutableString stringWithFormat:@"RLMResults <0x%lx> (\n", (long)self];
|
||||
unsigned long index = 0, skipped = 0;
|
||||
for (id obj in self) {
|
||||
NSString *sub;
|
||||
if ([obj respondsToSelector:@selector(descriptionWithMaxDepth:)]) {
|
||||
sub = [obj descriptionWithMaxDepth:RLMDescriptionMaxDepth - 1];
|
||||
}
|
||||
else {
|
||||
sub = [obj description];
|
||||
}
|
||||
|
||||
// Indent child objects
|
||||
NSString *objDescription = [sub stringByReplacingOccurrencesOfString:@"\n" withString:@"\n\t"];
|
||||
[mString appendFormat:@"\t[%lu] %@,\n", index++, objDescription];
|
||||
if (index >= maxObjects) {
|
||||
skipped = self.count - maxObjects;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove last comma and newline characters
|
||||
if(self.count > 0)
|
||||
[mString deleteCharactersInRange:NSMakeRange(mString.length-2, 2)];
|
||||
if (skipped) {
|
||||
[mString appendFormat:@"\n\t... %lu objects skipped.", skipped];
|
||||
}
|
||||
[mString appendFormat:@"\n)"];
|
||||
return [NSString stringWithString:mString];
|
||||
}
|
||||
|
||||
- (NSUInteger)indexInSource:(NSUInteger)index {
|
||||
return translateErrors([&] { return _results.get(index).get_index(); });
|
||||
}
|
||||
|
||||
- (realm::TableView)tableView {
|
||||
return translateErrors([&] { return _results.get_tableview(); });
|
||||
}
|
||||
|
||||
// The compiler complains about the method's argument type not matching due to
|
||||
// it not having the generic type attached, but it doesn't seem to be possible
|
||||
// to actually include the generic type
|
||||
// http://www.openradar.me/radar?id=6135653276319744
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wmismatched-parameter-types"
|
||||
- (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMResults *results, NSError *error))block {
|
||||
[_realm verifyNotificationsAreSupported];
|
||||
auto token = _results.async([self, block](std::exception_ptr err) {
|
||||
if (err) {
|
||||
try {
|
||||
rethrow_exception(err);
|
||||
}
|
||||
catch (...) {
|
||||
NSError *error;
|
||||
RLMRealmTranslateException(&error);
|
||||
block(nil, error);
|
||||
}
|
||||
}
|
||||
else {
|
||||
block(self, nil);
|
||||
}
|
||||
});
|
||||
|
||||
return [[RLMCancellationToken alloc] initWithToken:std::move(token)];
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
@end
|
||||
336
Example/Pods/Realm/Realm/RLMSchema.mm
generated
Normal file
336
Example/Pods/Realm/Realm/RLMSchema.mm
generated
Normal file
@@ -0,0 +1,336 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RLMSchema_Private.h"
|
||||
|
||||
#import "RLMAccessor.h"
|
||||
#import "RLMObject_Private.hpp"
|
||||
#import "RLMObjectSchema_Private.hpp"
|
||||
#import "RLMProperty_Private.h"
|
||||
#import "RLMRealm_Private.hpp"
|
||||
#import "RLMSwiftSupport.h"
|
||||
#import "RLMUtil.hpp"
|
||||
|
||||
#import "object_store.hpp"
|
||||
#import "schema.hpp"
|
||||
|
||||
#import <realm/group.hpp>
|
||||
|
||||
#import <objc/runtime.h>
|
||||
#include <mutex>
|
||||
|
||||
using namespace realm;
|
||||
|
||||
const uint64_t RLMNotVersioned = realm::ObjectStore::NotVersioned;
|
||||
|
||||
// RLMSchema private properties
|
||||
@interface RLMSchema ()
|
||||
@property (nonatomic, readwrite) NSMutableDictionary *objectSchemaByName;
|
||||
@end
|
||||
|
||||
static RLMSchema *s_sharedSchema = [[RLMSchema alloc] init];
|
||||
static NSMutableDictionary *s_localNameToClass = [[NSMutableDictionary alloc] init];
|
||||
static NSMutableDictionary *s_privateObjectSubclasses = [[NSMutableDictionary alloc] init];
|
||||
|
||||
static enum class SharedSchemaState {
|
||||
Uninitialized,
|
||||
Initializing,
|
||||
Initialized
|
||||
} s_sharedSchemaState = SharedSchemaState::Uninitialized;
|
||||
|
||||
// Caller must @synchronize on s_localNameToClass
|
||||
static RLMObjectSchema *RLMRegisterClass(Class cls) {
|
||||
if (RLMObjectSchema *schema = s_privateObjectSubclasses[[cls className]]) {
|
||||
return schema;
|
||||
}
|
||||
|
||||
auto prevState = s_sharedSchemaState;
|
||||
s_sharedSchemaState = SharedSchemaState::Initializing;
|
||||
RLMObjectSchema *schema = [RLMObjectSchema schemaForObjectClass:cls];
|
||||
s_sharedSchemaState = prevState;
|
||||
|
||||
// set standalone class on shared shema for standalone object creation
|
||||
schema.standaloneClass = RLMStandaloneAccessorClassForObjectClass(schema.objectClass, schema);
|
||||
|
||||
// override sharedSchema class methods for performance
|
||||
RLMReplaceSharedSchemaMethod(cls, schema);
|
||||
|
||||
s_privateObjectSubclasses[schema.className] = schema;
|
||||
if ([cls shouldIncludeInDefaultSchema]) {
|
||||
s_sharedSchema.objectSchemaByName[schema.className] = schema;
|
||||
}
|
||||
|
||||
return schema;
|
||||
}
|
||||
|
||||
// Caller must @synchronize on s_localNameToClass
|
||||
static void RLMRegisterClassLocalNames(Class *classes, NSUInteger count) {
|
||||
for (NSUInteger i = 0; i < count; i++) {
|
||||
Class cls = classes[i];
|
||||
|
||||
if (!RLMIsObjectSubclass(cls) || RLMIsGeneratedClass(cls)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NSString *className = NSStringFromClass(cls);
|
||||
if ([RLMSwiftSupport isSwiftClassName:className]) {
|
||||
className = [RLMSwiftSupport demangleClassName:className];
|
||||
}
|
||||
// NSStringFromClass demangles the names for top-level Swift classes
|
||||
// but not for nested classes. _T indicates it's a Swift symbol, t
|
||||
// indicates it's a type, and C indicates it's a class.
|
||||
else if ([className hasPrefix:@"_TtC"]) {
|
||||
@throw RLMException(@"RLMObject subclasses cannot be nested within other declarations. Please move %@ to global scope.", className);
|
||||
}
|
||||
|
||||
if (Class existingClass = s_localNameToClass[className]) {
|
||||
if (existingClass != cls) {
|
||||
@throw RLMException(@"RLMObject subclasses with the same name cannot be included twice in the same target. "
|
||||
@"Please make sure '%@' is only linked once to your current target.", className);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
s_localNameToClass[className] = cls;
|
||||
RLMReplaceClassNameMethod(cls, className);
|
||||
}
|
||||
}
|
||||
|
||||
@implementation RLMSchema {
|
||||
NSArray *_objectSchema;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_objectSchemaByName = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSArray *)objectSchema {
|
||||
if (!_objectSchema) {
|
||||
_objectSchema = [_objectSchemaByName allValues];
|
||||
}
|
||||
return _objectSchema;
|
||||
}
|
||||
|
||||
- (void)setObjectSchema:(NSArray *)objectSchema {
|
||||
_objectSchema = objectSchema;
|
||||
_objectSchemaByName = [NSMutableDictionary dictionaryWithCapacity:objectSchema.count];
|
||||
for (RLMObjectSchema *object in objectSchema) {
|
||||
[_objectSchemaByName setObject:object forKey:object.className];
|
||||
}
|
||||
}
|
||||
|
||||
- (RLMObjectSchema *)schemaForClassName:(NSString *)className {
|
||||
return _objectSchemaByName[className];
|
||||
}
|
||||
|
||||
- (RLMObjectSchema *)objectForKeyedSubscript:(__unsafe_unretained id<NSCopying> const)className {
|
||||
RLMObjectSchema *schema = _objectSchemaByName[className];
|
||||
if (!schema) {
|
||||
@throw RLMException(@"Object type '%@' not persisted in Realm", className);
|
||||
}
|
||||
return schema;
|
||||
}
|
||||
|
||||
+ (instancetype)schemaWithObjectClasses:(NSArray *)classes {
|
||||
NSUInteger count = classes.count;
|
||||
auto classArray = std::make_unique<__unsafe_unretained Class[]>(count);
|
||||
[classes getObjects:classArray.get() range:NSMakeRange(0, count)];
|
||||
|
||||
RLMSchema *schema = [[self alloc] init];
|
||||
@synchronized(s_localNameToClass) {
|
||||
RLMRegisterClassLocalNames(classArray.get(), count);
|
||||
|
||||
schema->_objectSchemaByName = [NSMutableDictionary dictionaryWithCapacity:count];
|
||||
for (Class cls in classes) {
|
||||
if (!RLMIsObjectSubclass(cls)) {
|
||||
@throw RLMException(@"Can't add non-Object type '%@' to a schema.", cls);
|
||||
}
|
||||
schema->_objectSchemaByName[[cls className]] = RLMRegisterClass(cls);
|
||||
}
|
||||
}
|
||||
|
||||
NSMutableArray *errors = [NSMutableArray new];
|
||||
// Verify that all of the targets of links are included in the class list
|
||||
[schema->_objectSchemaByName enumerateKeysAndObjectsUsingBlock:^(id, RLMObjectSchema *objectSchema, BOOL *) {
|
||||
for (RLMProperty *prop in objectSchema.properties) {
|
||||
if (prop.type != RLMPropertyTypeObject && prop.type != RLMPropertyTypeArray) {
|
||||
continue;
|
||||
}
|
||||
if (!schema->_objectSchemaByName[prop.objectClassName]) {
|
||||
[errors addObject:[NSString stringWithFormat:@"- '%@.%@' links to class '%@', which is missing from the list of classes to persist", objectSchema.className, prop.name, prop.objectClassName]];
|
||||
}
|
||||
}
|
||||
}];
|
||||
if (errors.count) {
|
||||
@throw RLMException(@"Invalid class subset list:\n%@", [errors componentsJoinedByString:@"\n"]);
|
||||
}
|
||||
|
||||
return schema;
|
||||
}
|
||||
|
||||
+ (RLMObjectSchema *)sharedSchemaForClass:(Class)cls {
|
||||
@synchronized(s_localNameToClass) {
|
||||
// We create instances of Swift objects during schema init, and they
|
||||
// obviously need to not also try to initialize the schema
|
||||
if (s_sharedSchemaState == SharedSchemaState::Initializing) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
RLMRegisterClassLocalNames(&cls, 1);
|
||||
return RLMRegisterClass(cls);
|
||||
}
|
||||
}
|
||||
|
||||
+ (instancetype)partialSharedSchema {
|
||||
return s_sharedSchema;
|
||||
}
|
||||
|
||||
// schema based on runtime objects
|
||||
+ (instancetype)sharedSchema {
|
||||
@synchronized(s_localNameToClass) {
|
||||
// We replace this method with one which just returns s_sharedSchema
|
||||
// once initialization is complete, but we still need to check if it's
|
||||
// already complete because it may have been done by another thread
|
||||
// while we were waiting for the lock
|
||||
if (s_sharedSchemaState == SharedSchemaState::Initialized) {
|
||||
return s_sharedSchema;
|
||||
}
|
||||
|
||||
if (s_sharedSchemaState == SharedSchemaState::Initializing) {
|
||||
@throw RLMException(@"Illegal recursive call of +[%@ %@]. Note: Properties of Swift `Object` classes must not be prepopulated with queried results from a Realm.", self, NSStringFromSelector(_cmd));
|
||||
}
|
||||
|
||||
s_sharedSchemaState = SharedSchemaState::Initializing;
|
||||
try {
|
||||
// Make sure we've discovered all classes
|
||||
{
|
||||
unsigned int numClasses;
|
||||
using malloc_ptr = std::unique_ptr<__unsafe_unretained Class[], decltype(&free)>;
|
||||
malloc_ptr classes(objc_copyClassList(&numClasses), &free);
|
||||
RLMRegisterClassLocalNames(classes.get(), numClasses);
|
||||
}
|
||||
|
||||
[s_localNameToClass enumerateKeysAndObjectsUsingBlock:^(NSString *, Class cls, BOOL *) {
|
||||
RLMRegisterClass(cls);
|
||||
}];
|
||||
}
|
||||
catch (...) {
|
||||
s_sharedSchemaState = SharedSchemaState::Uninitialized;
|
||||
throw;
|
||||
}
|
||||
|
||||
// Replace this method with one that doesn't need to acquire a lock
|
||||
Class metaClass = objc_getMetaClass(class_getName(self));
|
||||
IMP imp = imp_implementationWithBlock(^{ return s_sharedSchema; });
|
||||
class_replaceMethod(metaClass, @selector(sharedSchema), imp, "@@:");
|
||||
|
||||
s_sharedSchemaState = SharedSchemaState::Initialized;
|
||||
}
|
||||
|
||||
return s_sharedSchema;
|
||||
}
|
||||
|
||||
// schema based on tables in a realm
|
||||
+ (instancetype)dynamicSchemaFromObjectStoreSchema:(Schema &)objectStoreSchema {
|
||||
// cache descriptors for all subclasses of RLMObject
|
||||
NSMutableArray *schemaArray = [NSMutableArray arrayWithCapacity:objectStoreSchema.size()];
|
||||
for (auto &objectSchema : objectStoreSchema) {
|
||||
RLMObjectSchema *schema = [RLMObjectSchema objectSchemaForObjectStoreSchema:objectSchema];
|
||||
[schemaArray addObject:schema];
|
||||
}
|
||||
|
||||
// set class array and mapping
|
||||
RLMSchema *schema = [RLMSchema new];
|
||||
schema.objectSchema = schemaArray;
|
||||
return schema;
|
||||
}
|
||||
|
||||
+ (Class)classForString:(NSString *)className {
|
||||
if (Class cls = s_localNameToClass[className]) {
|
||||
return cls;
|
||||
}
|
||||
|
||||
if (Class cls = NSClassFromString(className)) {
|
||||
return RLMIsObjectSubclass(cls) ? cls : nil;
|
||||
}
|
||||
|
||||
// className might be the local name of a Swift class we haven't registered
|
||||
// yet, so scan them all then recheck
|
||||
{
|
||||
unsigned int numClasses;
|
||||
std::unique_ptr<__unsafe_unretained Class[], decltype(&free)> classes(objc_copyClassList(&numClasses), &free);
|
||||
RLMRegisterClassLocalNames(classes.get(), numClasses);
|
||||
}
|
||||
|
||||
return s_localNameToClass[className];
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone {
|
||||
RLMSchema *schema = [[RLMSchema allocWithZone:zone] init];
|
||||
schema->_objectSchemaByName = [[NSMutableDictionary allocWithZone:zone]
|
||||
initWithDictionary:_objectSchemaByName copyItems:YES];
|
||||
return schema;
|
||||
}
|
||||
|
||||
- (instancetype)shallowCopy {
|
||||
RLMSchema *schema = [[RLMSchema alloc] init];
|
||||
schema->_objectSchemaByName = [[NSMutableDictionary alloc] initWithCapacity:_objectSchemaByName.count];
|
||||
[_objectSchemaByName enumerateKeysAndObjectsUsingBlock:^(NSString *name, RLMObjectSchema *objectSchema, BOOL *) {
|
||||
schema->_objectSchemaByName[name] = [objectSchema shallowCopy];
|
||||
}];
|
||||
return schema;
|
||||
}
|
||||
|
||||
- (BOOL)isEqualToSchema:(RLMSchema *)schema {
|
||||
if (_objectSchemaByName.count != schema->_objectSchemaByName.count) {
|
||||
return NO;
|
||||
}
|
||||
__block BOOL matches = YES;
|
||||
[_objectSchemaByName enumerateKeysAndObjectsUsingBlock:^(NSString *name, RLMObjectSchema *objectSchema, BOOL *stop) {
|
||||
if (![schema->_objectSchemaByName[name] isEqualToObjectSchema:objectSchema]) {
|
||||
*stop = YES;
|
||||
matches = NO;
|
||||
}
|
||||
}];
|
||||
return matches;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
NSMutableString *objectSchemaString = [NSMutableString string];
|
||||
NSArray *sort = @[[NSSortDescriptor sortDescriptorWithKey:@"className" ascending:YES]];
|
||||
for (RLMObjectSchema *objectSchema in [self.objectSchema sortedArrayUsingDescriptors:sort]) {
|
||||
[objectSchemaString appendFormat:@"\t%@\n",
|
||||
[objectSchema.description stringByReplacingOccurrencesOfString:@"\n" withString:@"\n\t"]];
|
||||
}
|
||||
return [NSString stringWithFormat:@"Schema {\n%@}", objectSchemaString];
|
||||
}
|
||||
|
||||
- (std::unique_ptr<Schema>)objectStoreCopy {
|
||||
std::vector<realm::ObjectSchema> schema;
|
||||
schema.reserve(_objectSchemaByName.count);
|
||||
[_objectSchemaByName enumerateKeysAndObjectsUsingBlock:[&](NSString *, RLMObjectSchema *objectSchema, BOOL *) {
|
||||
schema.push_back(objectSchema.objectStoreCopy);
|
||||
}];
|
||||
return std::make_unique<realm::Schema>(std::move(schema));
|
||||
}
|
||||
|
||||
@end
|
||||
31
Example/Pods/Realm/Realm/RLMSwiftSupport.m
generated
Normal file
31
Example/Pods/Realm/Realm/RLMSwiftSupport.m
generated
Normal file
@@ -0,0 +1,31 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RLMSwiftSupport.h"
|
||||
|
||||
@implementation RLMSwiftSupport
|
||||
|
||||
+ (BOOL)isSwiftClassName:(NSString *)className {
|
||||
return [className rangeOfString:@"."].location != NSNotFound;
|
||||
}
|
||||
|
||||
+ (NSString *)demangleClassName:(NSString *)className {
|
||||
return [className substringFromIndex:[className rangeOfString:@"."].location + 1];
|
||||
}
|
||||
|
||||
@end
|
||||
48
Example/Pods/Realm/Realm/RLMUpdateChecker.mm
generated
Normal file
48
Example/Pods/Realm/Realm/RLMUpdateChecker.mm
generated
Normal file
@@ -0,0 +1,48 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RLMUpdateChecker.hpp"
|
||||
|
||||
#import "RLMRealm.h"
|
||||
#import "RLMUtil.hpp"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR && !defined(REALM_COCOA_VERSION)
|
||||
#import "RLMVersion.h"
|
||||
#endif
|
||||
|
||||
void RLMCheckForUpdates() {
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
if (getenv("REALM_DISABLE_UPDATE_CHECKER") || RLMIsRunningInPlayground()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto handler = ^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
if (error || ((NSHTTPURLResponse *)response).statusCode != 200) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *latestVersion = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
if (![REALM_COCOA_VERSION isEqualToString:latestVersion]) {
|
||||
NSLog(@"Version %@ of Realm is now available: https://github.com/realm/realm-cocoa/blob/v%@/CHANGELOG.md", latestVersion, latestVersion);
|
||||
}
|
||||
};
|
||||
|
||||
NSString *url = [NSString stringWithFormat:@"https://static.realm.io/update/cocoa?%@", REALM_COCOA_VERSION];
|
||||
[[NSURLSession.sharedSession dataTaskWithURL:[NSURL URLWithString:url] completionHandler:handler] resume];
|
||||
#endif
|
||||
}
|
||||
374
Example/Pods/Realm/Realm/RLMUtil.mm
generated
Normal file
374
Example/Pods/Realm/Realm/RLMUtil.mm
generated
Normal file
@@ -0,0 +1,374 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RLMUtil.hpp"
|
||||
|
||||
#import "RLMArray_Private.hpp"
|
||||
#import "RLMListBase.h"
|
||||
#import "RLMObjectSchema_Private.hpp"
|
||||
#import "RLMObjectStore.h"
|
||||
#import "RLMObject_Private.hpp"
|
||||
#import "RLMProperty_Private.h"
|
||||
#import "RLMSchema_Private.h"
|
||||
#import "RLMSwiftSupport.h"
|
||||
|
||||
#import <realm/mixed.hpp>
|
||||
#import <realm/table_view.hpp>
|
||||
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#if !defined(REALM_COCOA_VERSION)
|
||||
#import "RLMVersion.h"
|
||||
#endif
|
||||
|
||||
static inline bool nsnumber_is_like_integer(__unsafe_unretained NSNumber *const obj)
|
||||
{
|
||||
char data_type = [obj objCType][0];
|
||||
return data_type == *@encode(bool) ||
|
||||
data_type == *@encode(char) ||
|
||||
data_type == *@encode(short) ||
|
||||
data_type == *@encode(int) ||
|
||||
data_type == *@encode(long) ||
|
||||
data_type == *@encode(long long) ||
|
||||
data_type == *@encode(unsigned short) ||
|
||||
data_type == *@encode(unsigned int) ||
|
||||
data_type == *@encode(unsigned long) ||
|
||||
data_type == *@encode(unsigned long long);
|
||||
}
|
||||
|
||||
static inline bool nsnumber_is_like_bool(__unsafe_unretained NSNumber *const obj)
|
||||
{
|
||||
// @encode(BOOL) is 'B' on iOS 64 and 'c'
|
||||
// objcType is always 'c'. Therefore compare to "c".
|
||||
if ([obj objCType][0] == 'c') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (nsnumber_is_like_integer(obj)) {
|
||||
int value = [obj intValue];
|
||||
return value == 0 || value == 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool nsnumber_is_like_float(__unsafe_unretained NSNumber *const obj)
|
||||
{
|
||||
char data_type = [obj objCType][0];
|
||||
return data_type == *@encode(float) ||
|
||||
data_type == *@encode(short) ||
|
||||
data_type == *@encode(int) ||
|
||||
data_type == *@encode(long) ||
|
||||
data_type == *@encode(long long) ||
|
||||
data_type == *@encode(unsigned short) ||
|
||||
data_type == *@encode(unsigned int) ||
|
||||
data_type == *@encode(unsigned long) ||
|
||||
data_type == *@encode(unsigned long long) ||
|
||||
// A double is like float if it fits within float bounds
|
||||
(data_type == *@encode(double) && ABS([obj doubleValue]) <= FLT_MAX);
|
||||
}
|
||||
|
||||
static inline bool nsnumber_is_like_double(__unsafe_unretained NSNumber *const obj)
|
||||
{
|
||||
char data_type = [obj objCType][0];
|
||||
return data_type == *@encode(double) ||
|
||||
data_type == *@encode(float) ||
|
||||
data_type == *@encode(short) ||
|
||||
data_type == *@encode(int) ||
|
||||
data_type == *@encode(long) ||
|
||||
data_type == *@encode(long long) ||
|
||||
data_type == *@encode(unsigned short) ||
|
||||
data_type == *@encode(unsigned int) ||
|
||||
data_type == *@encode(unsigned long) ||
|
||||
data_type == *@encode(unsigned long long);
|
||||
}
|
||||
|
||||
static inline bool object_has_valid_type(__unsafe_unretained id const obj)
|
||||
{
|
||||
return ([obj isKindOfClass:[NSString class]] ||
|
||||
[obj isKindOfClass:[NSNumber class]] ||
|
||||
[obj isKindOfClass:[NSDate class]] ||
|
||||
[obj isKindOfClass:[NSData class]]);
|
||||
}
|
||||
|
||||
BOOL RLMIsObjectValidForProperty(__unsafe_unretained id const obj,
|
||||
__unsafe_unretained RLMProperty *const property) {
|
||||
if (property.optional && !RLMCoerceToNil(obj)) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
switch (property.type) {
|
||||
case RLMPropertyTypeString:
|
||||
return [obj isKindOfClass:[NSString class]];
|
||||
case RLMPropertyTypeBool:
|
||||
if ([obj isKindOfClass:[NSNumber class]]) {
|
||||
return nsnumber_is_like_bool(obj);
|
||||
}
|
||||
return NO;
|
||||
case RLMPropertyTypeDate:
|
||||
return [obj isKindOfClass:[NSDate class]];
|
||||
case RLMPropertyTypeInt:
|
||||
if (NSNumber *number = RLMDynamicCast<NSNumber>(obj)) {
|
||||
return nsnumber_is_like_integer(number);
|
||||
}
|
||||
return NO;
|
||||
case RLMPropertyTypeFloat:
|
||||
if (NSNumber *number = RLMDynamicCast<NSNumber>(obj)) {
|
||||
return nsnumber_is_like_float(number);
|
||||
}
|
||||
return NO;
|
||||
case RLMPropertyTypeDouble:
|
||||
if (NSNumber *number = RLMDynamicCast<NSNumber>(obj)) {
|
||||
return nsnumber_is_like_double(number);
|
||||
}
|
||||
return NO;
|
||||
case RLMPropertyTypeData:
|
||||
return [obj isKindOfClass:[NSData class]];
|
||||
case RLMPropertyTypeAny:
|
||||
return object_has_valid_type(obj);
|
||||
case RLMPropertyTypeObject: {
|
||||
// only NSNull, nil, or objects which derive from RLMObject and match the given
|
||||
// object class are valid
|
||||
RLMObjectBase *objBase = RLMDynamicCast<RLMObjectBase>(obj);
|
||||
return objBase && [objBase->_objectSchema.className isEqualToString:property.objectClassName];
|
||||
}
|
||||
case RLMPropertyTypeArray: {
|
||||
if (RLMArray *array = RLMDynamicCast<RLMArray>(obj)) {
|
||||
return [array.objectClassName isEqualToString:property.objectClassName];
|
||||
}
|
||||
if (RLMListBase *list = RLMDynamicCast<RLMListBase>(obj)) {
|
||||
return [list._rlmArray.objectClassName isEqualToString:property.objectClassName];
|
||||
}
|
||||
if ([obj conformsToProtocol:@protocol(NSFastEnumeration)]) {
|
||||
// check each element for compliance
|
||||
for (id el in (id<NSFastEnumeration>)obj) {
|
||||
RLMObjectBase *obj = RLMDynamicCast<RLMObjectBase>(el);
|
||||
if (!obj || ![obj->_objectSchema.className isEqualToString:property.objectClassName]) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
if (!obj || obj == NSNull.null) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
@throw RLMException(@"Invalid RLMPropertyType specified");
|
||||
}
|
||||
|
||||
NSDictionary *RLMDefaultValuesForObjectSchema(__unsafe_unretained RLMObjectSchema *const objectSchema) {
|
||||
if (!objectSchema.isSwiftClass) {
|
||||
return [objectSchema.objectClass defaultPropertyValues];
|
||||
}
|
||||
|
||||
NSMutableDictionary *defaults = nil;
|
||||
if ([objectSchema.objectClass isSubclassOfClass:RLMObject.class]) {
|
||||
defaults = [NSMutableDictionary dictionaryWithDictionary:[objectSchema.objectClass defaultPropertyValues]];
|
||||
}
|
||||
else {
|
||||
defaults = [NSMutableDictionary dictionary];
|
||||
}
|
||||
RLMObject *defaultObject = [[objectSchema.objectClass alloc] init];
|
||||
for (RLMProperty *prop in objectSchema.properties) {
|
||||
if (!defaults[prop.name] && defaultObject[prop.name]) {
|
||||
defaults[prop.name] = defaultObject[prop.name];
|
||||
}
|
||||
}
|
||||
return defaults;
|
||||
}
|
||||
|
||||
NSArray *RLMCollectionValueForKey(id<RLMFastEnumerable> collection, NSString *key) {
|
||||
size_t count = collection.count;
|
||||
if (count == 0) {
|
||||
return @[];
|
||||
}
|
||||
|
||||
RLMRealm *realm = collection.realm;
|
||||
RLMObjectSchema *objectSchema = collection.objectSchema;
|
||||
|
||||
NSMutableArray *results = [NSMutableArray arrayWithCapacity:count];
|
||||
if ([key isEqualToString:@"self"]) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
size_t rowIndex = [collection indexInSource:i];
|
||||
[results addObject:RLMCreateObjectAccessor(realm, objectSchema, rowIndex) ?: NSNull.null];
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
RLMObjectBase *accessor = [[objectSchema.accessorClass alloc] initWithRealm:realm schema:objectSchema];
|
||||
realm::Table *table = objectSchema.table;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
size_t rowIndex = [collection indexInSource:i];
|
||||
accessor->_row = (*table)[rowIndex];
|
||||
RLMInitializeSwiftAccessorGenerics(accessor);
|
||||
[results addObject:[accessor valueForKey:key] ?: NSNull.null];
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
void RLMCollectionSetValueForKey(id<RLMFastEnumerable> collection, NSString *key, id value) {
|
||||
realm::TableView tv = [collection tableView];
|
||||
if (tv.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
RLMRealm *realm = collection.realm;
|
||||
RLMObjectSchema *objectSchema = collection.objectSchema;
|
||||
RLMObjectBase *accessor = [[objectSchema.accessorClass alloc] initWithRealm:realm schema:objectSchema];
|
||||
for (size_t i = 0; i < tv.size(); i++) {
|
||||
accessor->_row = tv[i];
|
||||
RLMInitializeSwiftAccessorGenerics(accessor);
|
||||
[accessor setValue:value forKey:key];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static NSException *RLMException(NSString *reason, NSDictionary *additionalUserInfo) {
|
||||
NSMutableDictionary *userInfo = @{RLMRealmVersionKey: REALM_COCOA_VERSION,
|
||||
RLMRealmCoreVersionKey: @REALM_VERSION}.mutableCopy;
|
||||
if (additionalUserInfo != nil) {
|
||||
[userInfo addEntriesFromDictionary:additionalUserInfo];
|
||||
}
|
||||
NSException *e = [NSException exceptionWithName:RLMExceptionName
|
||||
reason:reason
|
||||
userInfo:userInfo];
|
||||
return e;
|
||||
}
|
||||
|
||||
NSException *RLMException(NSString *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
NSException *e = RLMException([[NSString alloc] initWithFormat:fmt arguments:args], @{});
|
||||
va_end(args);
|
||||
return e;
|
||||
}
|
||||
|
||||
NSException *RLMException(std::exception const& exception) {
|
||||
return RLMException(@"%@", @(exception.what()));
|
||||
}
|
||||
|
||||
NSError *RLMMakeError(RLMError code, std::exception const& exception) {
|
||||
return [NSError errorWithDomain:RLMErrorDomain
|
||||
code:code
|
||||
userInfo:@{NSLocalizedDescriptionKey: @(exception.what()),
|
||||
@"Error Code": @(code)}];
|
||||
}
|
||||
|
||||
NSError *RLMMakeError(RLMError code, const realm::util::File::AccessError& exception) {
|
||||
return [NSError errorWithDomain:RLMErrorDomain
|
||||
code:code
|
||||
userInfo:@{NSLocalizedDescriptionKey: @(exception.what()),
|
||||
NSFilePathErrorKey: @(exception.get_path().c_str()),
|
||||
@"Error Code": @(code)}];
|
||||
}
|
||||
|
||||
NSError *RLMMakeError(RLMError code, const realm::RealmFileException& exception) {
|
||||
return [NSError errorWithDomain:RLMErrorDomain
|
||||
code:code
|
||||
userInfo:@{NSLocalizedDescriptionKey: @(exception.what()),
|
||||
NSFilePathErrorKey: @(exception.path().c_str()),
|
||||
@"Error Code": @(code)}];
|
||||
}
|
||||
|
||||
NSError *RLMMakeError(std::system_error const& exception) {
|
||||
return [NSError errorWithDomain:RLMErrorDomain
|
||||
code:exception.code().value()
|
||||
userInfo:@{NSLocalizedDescriptionKey: @(exception.what()),
|
||||
@"Error Code": @(exception.code().value())}];
|
||||
}
|
||||
|
||||
NSError *RLMMakeError(NSException *exception) {
|
||||
return [NSError errorWithDomain:RLMErrorDomain
|
||||
code:0
|
||||
userInfo:@{NSLocalizedDescriptionKey: exception.reason}];
|
||||
}
|
||||
|
||||
void RLMSetErrorOrThrow(NSError *error, NSError **outError) {
|
||||
if (outError) {
|
||||
*outError = error;
|
||||
}
|
||||
else {
|
||||
NSString *msg = error.localizedDescription;
|
||||
if (error.userInfo[NSFilePathErrorKey]) {
|
||||
msg = [NSString stringWithFormat:@"%@: %@", error.userInfo[NSFilePathErrorKey], error.localizedDescription];
|
||||
}
|
||||
@throw RLMException(msg, @{NSUnderlyingErrorKey: error});
|
||||
}
|
||||
}
|
||||
|
||||
// Determines if class1 descends from class2
|
||||
static inline BOOL RLMIsSubclass(Class class1, Class class2) {
|
||||
class1 = class_getSuperclass(class1);
|
||||
return RLMIsKindOfClass(class1, class2);
|
||||
}
|
||||
|
||||
BOOL RLMIsObjectSubclass(Class klass) {
|
||||
return RLMIsSubclass(class_getSuperclass(klass), RLMObjectBase.class);
|
||||
}
|
||||
|
||||
BOOL RLMIsDebuggerAttached()
|
||||
{
|
||||
int name[] = {
|
||||
CTL_KERN,
|
||||
KERN_PROC,
|
||||
KERN_PROC_PID,
|
||||
getpid()
|
||||
};
|
||||
|
||||
struct kinfo_proc info;
|
||||
size_t info_size = sizeof(info);
|
||||
if (sysctl(name, sizeof(name)/sizeof(name[0]), &info, &info_size, NULL, 0) == -1) {
|
||||
NSLog(@"sysctl() failed: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return (info.kp_proc.p_flag & P_TRACED) != 0;
|
||||
}
|
||||
|
||||
BOOL RLMIsRunningInPlayground() {
|
||||
return [[NSBundle mainBundle].bundleIdentifier hasPrefix:@"com.apple.dt.playground."];
|
||||
}
|
||||
|
||||
id RLMMixedToObjc(realm::Mixed const& mixed) {
|
||||
switch (mixed.get_type()) {
|
||||
case realm::type_String:
|
||||
return RLMStringDataToNSString(mixed.get_string());
|
||||
case realm::type_Int: {
|
||||
return @(mixed.get_int());
|
||||
case realm::type_Float:
|
||||
return @(mixed.get_float());
|
||||
case realm::type_Double:
|
||||
return @(mixed.get_double());
|
||||
case realm::type_Bool:
|
||||
return @(mixed.get_bool());
|
||||
case realm::type_DateTime:
|
||||
return RLMDateTimeToNSDate(mixed.get_datetime());
|
||||
case realm::type_Binary: {
|
||||
return RLMBinaryDataToNSData(mixed.get_binary());
|
||||
}
|
||||
case realm::type_Link:
|
||||
case realm::type_LinkList:
|
||||
default:
|
||||
@throw RLMException(@"Invalid data type for RLMPropertyTypeAny property.");
|
||||
}
|
||||
}
|
||||
}
|
||||
27
Example/Pods/Realm/Realm/module.modulemap
generated
Normal file
27
Example/Pods/Realm/Realm/module.modulemap
generated
Normal file
@@ -0,0 +1,27 @@
|
||||
framework module Realm {
|
||||
umbrella header "Realm.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
|
||||
explicit module Private {
|
||||
header "RLMAccessor.h"
|
||||
header "RLMArray_Private.h"
|
||||
header "RLMListBase.h"
|
||||
header "RLMMigration_Private.h"
|
||||
header "RLMObjectSchema_Private.h"
|
||||
header "RLMObjectStore.h"
|
||||
header "RLMObject_Private.h"
|
||||
header "RLMOptionalBase.h"
|
||||
header "RLMProperty_Private.h"
|
||||
header "RLMRealmConfiguration_Private.h"
|
||||
header "RLMRealm_Private.h"
|
||||
header "RLMResults_Private.h"
|
||||
header "RLMSchema_Private.h"
|
||||
}
|
||||
|
||||
explicit module Dynamic {
|
||||
header "RLMRealm_Dynamic.h"
|
||||
header "RLMObjectBase_Dynamic.h"
|
||||
}
|
||||
}
|
||||
1300
Example/Pods/Realm/build.sh
generated
Executable file
1300
Example/Pods/Realm/build.sh
generated
Executable file
File diff suppressed because it is too large
Load Diff
BIN
Example/Pods/Realm/core/librealm-ios.a
generated
Normal file
BIN
Example/Pods/Realm/core/librealm-ios.a
generated
Normal file
Binary file not shown.
55
Example/Pods/Realm/include/RLMAnalytics.hpp
generated
Normal file
55
Example/Pods/Realm/include/RLMAnalytics.hpp
generated
Normal file
@@ -0,0 +1,55 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2015 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.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Asynchronously submits build information to Realm if running in an iOS
|
||||
// simulator or on OS X if a debugger is attached. Does nothing if running on an
|
||||
// iOS / watchOS device or if a debugger is *not* attached.
|
||||
//
|
||||
// To be clear: this does *not* run when your app is in production or on
|
||||
// your end-user’s devices; it will only run in the simulator or when a debugger
|
||||
// is attached.
|
||||
//
|
||||
// Why are we doing this? In short, because it helps us build a better product
|
||||
// for you. None of the data personally identifies you, your employer or your
|
||||
// app, but it *will* help us understand what language you use, what iOS
|
||||
// versions you target, etc. Having this info will help prioritizing our time,
|
||||
// adding new features and deprecating old features. Collecting an anonymized
|
||||
// bundle & anonymized MAC is the only way for us to count actual usage of the
|
||||
// other metrics accurately. If we don’t have a way to deduplicate the info
|
||||
// reported, it will be useless, as a single developer building their Swift app
|
||||
// 10 times would report 10 times more than a single Objective-C developer that
|
||||
// only builds once, making the data all but useless.
|
||||
// No one likes sharing data unless it’s necessary, we get it, and we’ve
|
||||
// debated adding this for a long long time. Since Realm is a free product
|
||||
// without an email signup, we feel this is a necessary step so we can collect
|
||||
// relevant data to build a better product for you. If you truly, absolutely
|
||||
// feel compelled to not send this data back to Realm, then you can set an env
|
||||
// variable named REALM_DISABLE_ANALYTICS. Since Realm is free we believe
|
||||
// letting these analytics run is a small price to pay for the product & support
|
||||
// we give you.
|
||||
//
|
||||
// Currently the following information is reported:
|
||||
// - What version of Realm is being used, and from which language (obj-c or Swift).
|
||||
// - What version of OS X it's running on (in case Xcode aggressively drops
|
||||
// support for older versions again, we need to know what we need to support).
|
||||
// - The minimum iOS/OS X version that the application is targeting (again, to
|
||||
// help us decide what versions we need to support).
|
||||
// - An anonymous MAC address and bundle ID to aggregate the other information on.
|
||||
// - What version of Swift is being used (if applicable).
|
||||
|
||||
void RLMSendAnalytics();
|
||||
105
Example/Pods/Realm/include/RLMArray_Private.hpp
generated
Normal file
105
Example/Pods/Realm/include/RLMArray_Private.hpp
generated
Normal file
@@ -0,0 +1,105 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RLMArray_Private.h"
|
||||
#import <Realm/RLMResults.h>
|
||||
|
||||
#import <memory>
|
||||
#import <vector>
|
||||
|
||||
namespace realm {
|
||||
class LinkView;
|
||||
class Results;
|
||||
class TableView;
|
||||
struct SortOrder;
|
||||
|
||||
namespace util {
|
||||
template<typename T> class bind_ptr;
|
||||
}
|
||||
typedef util::bind_ptr<LinkView> LinkViewRef;
|
||||
}
|
||||
|
||||
@class RLMObjectBase;
|
||||
@class RLMObjectSchema;
|
||||
class RLMObservationInfo;
|
||||
|
||||
@protocol RLMFastEnumerable
|
||||
@property (nonatomic, readonly) RLMRealm *realm;
|
||||
@property (nonatomic, readonly) RLMObjectSchema *objectSchema;
|
||||
@property (nonatomic, readonly) NSUInteger count;
|
||||
|
||||
- (NSUInteger)indexInSource:(NSUInteger)index;
|
||||
- (realm::TableView)tableView;
|
||||
@end
|
||||
|
||||
@interface RLMArray () {
|
||||
@protected
|
||||
NSString *_objectClassName;
|
||||
@public
|
||||
// The name of the property which this RLMArray represents
|
||||
NSString *_key;
|
||||
__weak RLMObjectBase *_parentObject;
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
//
|
||||
// LinkView backed RLMArray subclass
|
||||
//
|
||||
@interface RLMArrayLinkView : RLMArray <RLMFastEnumerable>
|
||||
@property (nonatomic, unsafe_unretained) RLMObjectSchema *objectSchema;
|
||||
|
||||
+ (RLMArrayLinkView *)arrayWithObjectClassName:(NSString *)objectClassName
|
||||
view:(realm::LinkViewRef)view
|
||||
realm:(RLMRealm *)realm
|
||||
key:(NSString *)key
|
||||
parentSchema:(RLMObjectSchema *)parentSchema;
|
||||
|
||||
// deletes all objects in the RLMArray from their containing realms
|
||||
- (void)deleteObjectsFromRealm;
|
||||
@end
|
||||
|
||||
void RLMValidateArrayObservationKey(NSString *keyPath, RLMArray *array);
|
||||
|
||||
// Initialize the observation info for an array if needed
|
||||
void RLMEnsureArrayObservationInfo(std::unique_ptr<RLMObservationInfo>& info, NSString *keyPath, RLMArray *array, id observed);
|
||||
|
||||
|
||||
//
|
||||
// RLMResults private methods
|
||||
//
|
||||
@interface RLMResults () <RLMFastEnumerable>
|
||||
+ (instancetype)resultsWithObjectSchema:(RLMObjectSchema *)objectSchema
|
||||
results:(realm::Results)results;
|
||||
|
||||
- (void)deleteObjectsFromRealm;
|
||||
@end
|
||||
|
||||
// An object which encapulates the shared logic for fast-enumerating RLMArray
|
||||
// and RLMResults, and has a buffer to store strong references to the current
|
||||
// set of enumerated items
|
||||
@interface RLMFastEnumerator : NSObject
|
||||
- (instancetype)initWithCollection:(id<RLMFastEnumerable>)collection objectSchema:(RLMObjectSchema *)objectSchema;
|
||||
|
||||
// Detach this enumerator from the source collection. Must be called before the
|
||||
// source collection is changed.
|
||||
- (void)detach;
|
||||
|
||||
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
|
||||
count:(NSUInteger)len;
|
||||
@end
|
||||
49
Example/Pods/Realm/include/RLMObjectSchema_Private.hpp
generated
Normal file
49
Example/Pods/Realm/include/RLMObjectSchema_Private.hpp
generated
Normal file
@@ -0,0 +1,49 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RLMObjectSchema_Private.h"
|
||||
|
||||
#import "object_schema.hpp"
|
||||
#import "RLMObject_Private.hpp"
|
||||
|
||||
#import <realm/row.hpp>
|
||||
#import <vector>
|
||||
|
||||
namespace realm {
|
||||
class Table;
|
||||
}
|
||||
|
||||
class RLMObservationInfo;
|
||||
|
||||
// RLMObjectSchema private
|
||||
@interface RLMObjectSchema () {
|
||||
@public
|
||||
std::vector<RLMObservationInfo *> _observedObjects;
|
||||
}
|
||||
@property (nonatomic) realm::Table *table;
|
||||
|
||||
// shallow copy reusing properties and property map
|
||||
- (instancetype)shallowCopy;
|
||||
|
||||
// create realm::ObjectSchema copy
|
||||
- (realm::ObjectSchema)objectStoreCopy;
|
||||
|
||||
// initialize with realm::ObjectSchema
|
||||
+ (instancetype)objectSchemaForObjectStoreSchema:(realm::ObjectSchema &)objectSchema;
|
||||
|
||||
@end
|
||||
52
Example/Pods/Realm/include/RLMObject_Private.hpp
generated
Normal file
52
Example/Pods/Realm/include/RLMObject_Private.hpp
generated
Normal file
@@ -0,0 +1,52 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RLMObject_Private.h"
|
||||
|
||||
#import "RLMRealm_Private.hpp"
|
||||
|
||||
#import <realm/link_view.hpp> // required by row.hpp
|
||||
#import <realm/row.hpp>
|
||||
|
||||
class RLMObservationInfo;
|
||||
|
||||
// RLMObject accessor and read/write realm
|
||||
@interface RLMObjectBase () {
|
||||
@public
|
||||
realm::Row _row;
|
||||
RLMObservationInfo *_observationInfo;
|
||||
}
|
||||
@end
|
||||
|
||||
// throw an exception if the object is invalidated or on the wrong thread
|
||||
static inline void RLMVerifyAttached(__unsafe_unretained RLMObjectBase *const obj) {
|
||||
if (!obj->_row.is_attached()) {
|
||||
@throw RLMException(@"Object has been deleted or invalidated.");
|
||||
}
|
||||
[obj->_realm verifyThread];
|
||||
}
|
||||
|
||||
// throw an exception if the object can't be modified for any reason
|
||||
static inline void RLMVerifyInWriteTransaction(__unsafe_unretained RLMObjectBase *const obj) {
|
||||
// first verify is attached
|
||||
RLMVerifyAttached(obj);
|
||||
|
||||
if (!obj->_realm.inWriteTransaction) {
|
||||
@throw RLMException(@"Attempting to modify object outside of a write transaction - call beginWriteTransaction on an RLMRealm instance first.");
|
||||
}
|
||||
}
|
||||
144
Example/Pods/Realm/include/RLMObservation.hpp
generated
Normal file
144
Example/Pods/Realm/include/RLMObservation.hpp
generated
Normal file
@@ -0,0 +1,144 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2015 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 <Foundation/Foundation.h>
|
||||
|
||||
#import "binding_context.hpp"
|
||||
|
||||
#import <Realm/RLMDefines.h>
|
||||
|
||||
#import <realm/link_view.hpp> // required by row.hpp
|
||||
#import <realm/row.hpp>
|
||||
|
||||
@class RLMObjectSchema, RLMObjectBase, RLMRealm, RLMSchema, RLMProperty;
|
||||
|
||||
namespace realm {
|
||||
class History;
|
||||
class SharedGroup;
|
||||
}
|
||||
|
||||
// RLMObservationInfo stores all of the KVO-related data for RLMObjectBase and
|
||||
// RLMArray. There is a one-to-one relationship between observed objects and
|
||||
// RLMObservationInfo instances, so it could be folded into RLMObjectBase, and
|
||||
// is a separate class mostly to avoid making all accessor objects far larger.
|
||||
//
|
||||
// RLMObjectSchema stores a vector of pointers to the first observation info
|
||||
// created for each row. If there are multiple observation infos for a single
|
||||
// row (such as if there are multiple observed objects backed by a single row,
|
||||
// or if both an object and an array property of that object are observed),
|
||||
// they're stored in an intrusive doubly-linked-list in the `next` and `prev`
|
||||
// members. This is done primarily to make it simpler and faster to loop over
|
||||
// all of the observed objects for a single row, as that needs to be done for
|
||||
// every change.
|
||||
class RLMObservationInfo {
|
||||
public:
|
||||
RLMObservationInfo(id object);
|
||||
RLMObservationInfo(RLMObjectSchema *objectSchema, std::size_t row, id object);
|
||||
~RLMObservationInfo();
|
||||
|
||||
realm::Row const& getRow() const {
|
||||
return row;
|
||||
}
|
||||
|
||||
RLMObjectSchema *getObjectSchema() const {
|
||||
return objectSchema;
|
||||
}
|
||||
|
||||
// Send willChange/didChange notifications to all observers for this object/row
|
||||
// Sends the array versions if indexes is non-nil, normal versions otherwise
|
||||
void willChange(NSString *key, NSKeyValueChange kind=NSKeyValueChangeSetting, NSIndexSet *indexes=nil) const;
|
||||
void didChange(NSString *key, NSKeyValueChange kind=NSKeyValueChangeSetting, NSIndexSet *indexes=nil) const;
|
||||
|
||||
bool isForRow(size_t ndx) const {
|
||||
return row && row.get_index() == ndx;
|
||||
}
|
||||
|
||||
void recordObserver(realm::Row& row, RLMObjectSchema *objectSchema, NSString *keyPath);
|
||||
void removeObserver();
|
||||
bool hasObservers() const { return observerCount > 0; }
|
||||
|
||||
// valueForKey: on observed object and array properties needs to return the
|
||||
// same object each time for KVO to work at all. Doing this all the time
|
||||
// requires some odd semantics to avoid reference cycles, so instead we do
|
||||
// it only to the extent specifically required by KVO. In addition, we
|
||||
// need to continue to return the same object even if this row is deleted,
|
||||
// or deleting an object with active observers will explode horribly.
|
||||
// Once prepareForInvalidation() is called, valueForKey() will always return
|
||||
// the cached value for object and array properties without checking the
|
||||
// backing row to verify it's up-to-date.
|
||||
//
|
||||
// prepareForInvalidation() must be called on the head of the linked list
|
||||
// (i.e. on the object pointed to directly by the object schema)
|
||||
id valueForKey(NSString *key);
|
||||
|
||||
void prepareForInvalidation();
|
||||
|
||||
private:
|
||||
// Doubly-linked-list of observed objects for the same row as this
|
||||
RLMObservationInfo *next = nullptr;
|
||||
RLMObservationInfo *prev = nullptr;
|
||||
|
||||
// Row being observed
|
||||
realm::Row row;
|
||||
RLMObjectSchema *objectSchema;
|
||||
|
||||
// Object doing the observing
|
||||
__unsafe_unretained id object;
|
||||
|
||||
// valueForKey: hack
|
||||
bool invalidated = false;
|
||||
size_t observerCount = 0;
|
||||
NSString *lastKey = nil;
|
||||
__unsafe_unretained RLMProperty *lastProp = nil;
|
||||
|
||||
// objects returned from valueForKey() to keep them alive in case observers
|
||||
// are added and so that they can still be accessed after row is detached
|
||||
NSMutableDictionary *cachedObjects;
|
||||
|
||||
void setRow(realm::Table &table, size_t newRow);
|
||||
|
||||
template<typename F>
|
||||
void forEach(F&& f) const {
|
||||
for (auto info = prev; info; info = info->prev)
|
||||
f(info->object);
|
||||
for (auto info = this; info; info = info->next)
|
||||
f(info->object);
|
||||
}
|
||||
|
||||
// Default move/copy constructors don't work due to the intrusive linked
|
||||
// list and we don't need them
|
||||
RLMObservationInfo(RLMObservationInfo const&) = delete;
|
||||
RLMObservationInfo(RLMObservationInfo&&) = delete;
|
||||
RLMObservationInfo& operator=(RLMObservationInfo const&) = delete;
|
||||
RLMObservationInfo& operator=(RLMObservationInfo&&) = delete;
|
||||
};
|
||||
|
||||
// Get the the observation info chain for the given row
|
||||
// Will simply return info if it's non-null, and will search ojectSchema's array
|
||||
// for a matching one otherwise, and return null if there are none
|
||||
RLMObservationInfo *RLMGetObservationInfo(RLMObservationInfo *info, size_t row, RLMObjectSchema *objectSchema);
|
||||
|
||||
// delete all objects from a single table with change notifications
|
||||
void RLMClearTable(RLMObjectSchema *realm);
|
||||
|
||||
// invoke the block, sending notifications for cascading deletes/link nullifications
|
||||
void RLMTrackDeletions(RLMRealm *realm, dispatch_block_t block);
|
||||
|
||||
std::vector<realm::BindingContext::ObserverState> RLMGetObservedRows(NSArray RLM_GENERIC(RLMObjectSchema *) *schema);
|
||||
void RLMWillChange(std::vector<realm::BindingContext::ObserverState> const& observed, std::vector<void *> const& invalidated);
|
||||
void RLMDidChange(std::vector<realm::BindingContext::ObserverState> const& observed, std::vector<void *> const& invalidated);
|
||||
21
Example/Pods/Realm/include/RLMPredicateUtil.hpp
generated
Normal file
21
Example/Pods/Realm/include/RLMPredicateUtil.hpp
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2015 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 <Foundation/Foundation.h>
|
||||
|
||||
using ExpressionVisitor = NSExpression *(*)(NSExpression *);
|
||||
NSPredicate *transformPredicate(NSPredicate *, ExpressionVisitor);
|
||||
44
Example/Pods/Realm/include/RLMQueryUtil.hpp
generated
Normal file
44
Example/Pods/Realm/include/RLMQueryUtil.hpp
generated
Normal file
@@ -0,0 +1,44 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <Foundation/Foundation.h>
|
||||
#import <vector>
|
||||
|
||||
namespace realm {
|
||||
class Query;
|
||||
struct SortOrder;
|
||||
class Table;
|
||||
class TableView;
|
||||
}
|
||||
|
||||
@class RLMObjectSchema;
|
||||
@class RLMProperty;
|
||||
@class RLMSchema;
|
||||
|
||||
extern NSString * const RLMPropertiesComparisonTypeMismatchException;
|
||||
extern NSString * const RLMUnsupportedTypesFoundInPropertyComparisonException;
|
||||
|
||||
// apply the given predicate to the passed in query, returning the updated query
|
||||
void RLMUpdateQueryWithPredicate(realm::Query *query, NSPredicate *predicate, RLMSchema *schema,
|
||||
RLMObjectSchema *objectSchema);
|
||||
|
||||
// return property - throw for invalid column name
|
||||
RLMProperty *RLMValidatedProperty(RLMObjectSchema *objectSchema, NSString *columnName);
|
||||
|
||||
// validate the array of RLMSortDescriptors and convert it to a realm::SortOrder
|
||||
realm::SortOrder RLMSortOrderFromDescriptors(RLMObjectSchema *objectSchema, NSArray *descriptors);
|
||||
42
Example/Pods/Realm/include/RLMRealmUtil.hpp
generated
Normal file
42
Example/Pods/Realm/include/RLMRealmUtil.hpp
generated
Normal file
@@ -0,0 +1,42 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <Foundation/Foundation.h>
|
||||
#import <memory>
|
||||
#import <string>
|
||||
|
||||
@class RLMRealm;
|
||||
|
||||
namespace realm {
|
||||
class BindingContext;
|
||||
}
|
||||
|
||||
// Add a Realm to the weak cache
|
||||
void RLMCacheRealm(std::string const& path, RLMRealm *realm);
|
||||
// Get a Realm for the given path which can be used on the current thread
|
||||
RLMRealm *RLMGetThreadLocalCachedRealmForPath(std::string const& path);
|
||||
// Get a Realm for the given path
|
||||
RLMRealm *RLMGetAnyCachedRealmForPath(std::string const& path);
|
||||
// Clear the weak cache of Realms
|
||||
void RLMClearRealmCache();
|
||||
|
||||
// Install an uncaught exception handler that cancels write transactions
|
||||
// for all cached realms on the current thread
|
||||
void RLMInstallUncaughtExceptionHandler();
|
||||
|
||||
std::unique_ptr<realm::BindingContext> RLMCreateBindingContext(RLMRealm *realm);
|
||||
38
Example/Pods/Realm/include/RLMRealm_Private.hpp
generated
Normal file
38
Example/Pods/Realm/include/RLMRealm_Private.hpp
generated
Normal file
@@ -0,0 +1,38 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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.h"
|
||||
#import "RLMUtil.hpp"
|
||||
#import "shared_realm.hpp"
|
||||
|
||||
#import <realm/group.hpp>
|
||||
|
||||
namespace realm {
|
||||
class Group;
|
||||
class Realm;
|
||||
typedef std::shared_ptr<realm::Realm> SharedRealm;
|
||||
}
|
||||
|
||||
@interface RLMRealm () {
|
||||
@public
|
||||
realm::SharedRealm _realm;
|
||||
}
|
||||
|
||||
// FIXME - group should not be exposed
|
||||
@property (nonatomic, readonly) realm::Group *group;
|
||||
@end
|
||||
31
Example/Pods/Realm/include/RLMSchema_Private.hpp
generated
Normal file
31
Example/Pods/Realm/include/RLMSchema_Private.hpp
generated
Normal file
@@ -0,0 +1,31 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2015 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 "RLMSchema_Private.h"
|
||||
|
||||
#import <memory>
|
||||
|
||||
namespace realm {
|
||||
class Schema;
|
||||
class ObjectSchema;
|
||||
}
|
||||
|
||||
@interface RLMSchema ()
|
||||
+ (instancetype)dynamicSchemaFromObjectStoreSchema:(realm::Schema &)objectStoreSchema;
|
||||
- (std::unique_ptr<realm::Schema>)objectStoreCopy;
|
||||
@end
|
||||
20
Example/Pods/Realm/include/RLMUpdateChecker.hpp
generated
Normal file
20
Example/Pods/Realm/include/RLMUpdateChecker.hpp
generated
Normal file
@@ -0,0 +1,20 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Asynchronously check for updates to Realm if running on a simulator
|
||||
void RLMCheckForUpdates();
|
||||
174
Example/Pods/Realm/include/RLMUtil.hpp
generated
Normal file
174
Example/Pods/Realm/include/RLMUtil.hpp
generated
Normal file
@@ -0,0 +1,174 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <Realm/RLMConstants.h>
|
||||
#import <Realm/RLMOptionalBase.h>
|
||||
#import <objc/runtime.h>
|
||||
|
||||
#import <realm/array.hpp>
|
||||
#import <realm/binary_data.hpp>
|
||||
#import <realm/datetime.hpp>
|
||||
#import <realm/string_data.hpp>
|
||||
#import <realm/util/file.hpp>
|
||||
|
||||
namespace realm {
|
||||
class Mixed;
|
||||
}
|
||||
|
||||
@class RLMObjectSchema;
|
||||
@class RLMProperty;
|
||||
@protocol RLMFastEnumerable;
|
||||
|
||||
namespace realm {
|
||||
class RealmFileException;
|
||||
}
|
||||
|
||||
__attribute__((format(NSString, 1, 2)))
|
||||
NSException *RLMException(NSString *fmt, ...);
|
||||
NSException *RLMException(std::exception const& exception);
|
||||
|
||||
NSError *RLMMakeError(RLMError code, std::exception const& exception);
|
||||
NSError *RLMMakeError(RLMError code, const realm::util::File::AccessError&);
|
||||
NSError *RLMMakeError(RLMError code, const realm::RealmFileException&);
|
||||
NSError *RLMMakeError(std::system_error const& exception);
|
||||
NSError *RLMMakeError(NSException *exception);
|
||||
|
||||
void RLMSetErrorOrThrow(NSError *error, NSError **outError);
|
||||
|
||||
// returns if the object can be inserted as the given type
|
||||
BOOL RLMIsObjectValidForProperty(id obj, RLMProperty *prop);
|
||||
|
||||
// gets default values for the given schema (+defaultPropertyValues)
|
||||
// merges with native property defaults if Swift class
|
||||
NSDictionary *RLMDefaultValuesForObjectSchema(RLMObjectSchema *objectSchema);
|
||||
|
||||
NSArray *RLMCollectionValueForKey(id<RLMFastEnumerable> collection, NSString *key);
|
||||
|
||||
void RLMCollectionSetValueForKey(id<RLMFastEnumerable> collection, NSString *key, id value);
|
||||
|
||||
BOOL RLMIsDebuggerAttached();
|
||||
BOOL RLMIsRunningInPlayground();
|
||||
|
||||
// C version of isKindOfClass
|
||||
static inline BOOL RLMIsKindOfClass(Class class1, Class class2) {
|
||||
while (class1) {
|
||||
if (class1 == class2) return YES;
|
||||
class1 = class_getSuperclass(class1);
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Returns whether the class is an indirect descendant of RLMObjectBase
|
||||
BOOL RLMIsObjectSubclass(Class klass);
|
||||
|
||||
template<typename T>
|
||||
static inline T *RLMDynamicCast(__unsafe_unretained id obj) {
|
||||
if ([obj isKindOfClass:[T class]]) {
|
||||
return obj;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline T RLMCoerceToNil(__unsafe_unretained T obj) {
|
||||
if (static_cast<id>(obj) == NSNull.null) {
|
||||
return nil;
|
||||
}
|
||||
else if (__unsafe_unretained auto optional = RLMDynamicCast<RLMOptionalBase>(obj)) {
|
||||
return RLMCoerceToNil(optional.underlyingValue);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Translate an rlmtype to a string representation
|
||||
static inline NSString *RLMTypeToString(RLMPropertyType type) {
|
||||
switch (type) {
|
||||
case RLMPropertyTypeString:
|
||||
return @"string";
|
||||
case RLMPropertyTypeInt:
|
||||
return @"int";
|
||||
case RLMPropertyTypeBool:
|
||||
return @"bool";
|
||||
case RLMPropertyTypeDate:
|
||||
return @"date";
|
||||
case RLMPropertyTypeData:
|
||||
return @"data";
|
||||
case RLMPropertyTypeDouble:
|
||||
return @"double";
|
||||
case RLMPropertyTypeFloat:
|
||||
return @"float";
|
||||
case RLMPropertyTypeAny:
|
||||
return @"any";
|
||||
case RLMPropertyTypeObject:
|
||||
return @"object";
|
||||
case RLMPropertyTypeArray:
|
||||
return @"array";
|
||||
}
|
||||
return @"Unknown";
|
||||
}
|
||||
|
||||
// String conversion utilities
|
||||
static inline NSString * RLMStringDataToNSString(realm::StringData stringData) {
|
||||
static_assert(sizeof(NSUInteger) >= sizeof(size_t),
|
||||
"Need runtime overflow check for size_t to NSUInteger conversion");
|
||||
if (stringData.is_null()) {
|
||||
return nil;
|
||||
}
|
||||
else {
|
||||
return [[NSString alloc] initWithBytes:stringData.data()
|
||||
length:stringData.size()
|
||||
encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
}
|
||||
|
||||
static inline realm::StringData RLMStringDataWithNSString(__unsafe_unretained NSString *const string) {
|
||||
static_assert(sizeof(size_t) >= sizeof(NSUInteger),
|
||||
"Need runtime overflow check for NSUInteger to size_t conversion");
|
||||
return realm::StringData(string.UTF8String,
|
||||
[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
|
||||
}
|
||||
|
||||
// Binary convertion utilities
|
||||
static inline NSData *RLMBinaryDataToNSData(realm::BinaryData binaryData) {
|
||||
return binaryData ? [NSData dataWithBytes:binaryData.data() length:binaryData.size()] : nil;
|
||||
}
|
||||
|
||||
static inline realm::BinaryData RLMBinaryDataForNSData(__unsafe_unretained NSData *const data) {
|
||||
// this is necessary to ensure that the empty NSData isn't treated by core as the null realm::BinaryData
|
||||
// because data.bytes == 0 when data.length == 0
|
||||
// the casting bit ensures that we create a data with a non-null pointer
|
||||
auto bytes = static_cast<const char *>(data.bytes) ?: static_cast<char *>((__bridge void *)data);
|
||||
return realm::BinaryData(bytes, data.length);
|
||||
}
|
||||
|
||||
// Date convertion utilities
|
||||
static inline NSDate *RLMDateTimeToNSDate(realm::DateTime dateTime) {
|
||||
auto timeInterval = static_cast<NSTimeInterval>(dateTime.get_datetime());
|
||||
return [NSDate dateWithTimeIntervalSince1970:timeInterval];
|
||||
}
|
||||
|
||||
static inline realm::DateTime RLMDateTimeForNSDate(__unsafe_unretained NSDate *const date) {
|
||||
auto time = static_cast<int64_t>(date.timeIntervalSince1970);
|
||||
return realm::DateTime(time);
|
||||
}
|
||||
|
||||
static inline NSUInteger RLMConvertNotFound(size_t index) {
|
||||
return index == realm::not_found ? NSNotFound : index;
|
||||
}
|
||||
|
||||
id RLMMixedToObjc(realm::Mixed const& value);
|
||||
64
Example/Pods/Realm/include/Realm/RLMAccessor.h
generated
Normal file
64
Example/Pods/Realm/include/Realm/RLMAccessor.h
generated
Normal file
@@ -0,0 +1,64 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <Foundation/Foundation.h>
|
||||
|
||||
#import <Realm/RLMDefines.h>
|
||||
|
||||
@class RLMObjectSchema, RLMProperty, RLMObjectBase, RLMProperty;
|
||||
|
||||
#ifdef __cplusplus
|
||||
typedef NSUInteger RLMCreationOptions;
|
||||
#else
|
||||
typedef NS_OPTIONS(NSUInteger, RLMCreationOptions);
|
||||
#endif
|
||||
|
||||
RLM_ASSUME_NONNULL_BEGIN
|
||||
|
||||
//
|
||||
// Accessors Class Creation/Caching
|
||||
//
|
||||
|
||||
// get accessor classes for an object class - generates classes if not cached
|
||||
Class RLMAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema, NSString *prefix);
|
||||
Class RLMStandaloneAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema);
|
||||
|
||||
// Check if a given class is a generated accessor class
|
||||
bool RLMIsGeneratedClass(Class cls);
|
||||
|
||||
//
|
||||
// Dynamic getters/setters
|
||||
//
|
||||
FOUNDATION_EXTERN void RLMDynamicValidatedSet(RLMObjectBase *obj, NSString *propName, id __nullable val);
|
||||
FOUNDATION_EXTERN RLMProperty *RLMValidatedGetProperty(RLMObjectBase *obj, NSString *propName);
|
||||
FOUNDATION_EXTERN id __nullable RLMDynamicGet(RLMObjectBase *obj, RLMProperty *prop);
|
||||
|
||||
// by property/column
|
||||
FOUNDATION_EXTERN void RLMDynamicSet(RLMObjectBase *obj, RLMProperty *prop, id val, RLMCreationOptions options);
|
||||
|
||||
//
|
||||
// Class modification
|
||||
//
|
||||
|
||||
// Replace className method for the given class
|
||||
void RLMReplaceClassNameMethod(Class accessorClass, NSString *className);
|
||||
|
||||
// Replace sharedSchema method for the given class
|
||||
void RLMReplaceSharedSchemaMethod(Class accessorClass, RLMObjectSchema * __nullable schema);
|
||||
|
||||
RLM_ASSUME_NONNULL_END
|
||||
364
Example/Pods/Realm/include/Realm/RLMArray.h
generated
Normal file
364
Example/Pods/Realm/include/Realm/RLMArray.h
generated
Normal file
@@ -0,0 +1,364 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <Foundation/Foundation.h>
|
||||
|
||||
#import <Realm/RLMCollection.h>
|
||||
#import <Realm/RLMDefines.h>
|
||||
|
||||
RLM_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class RLMObject, RLMRealm, RLMResults RLM_GENERIC_COLLECTION, RLMNotificationToken;
|
||||
|
||||
/**
|
||||
|
||||
RLMArray is the container type in Realm used to define to-many relationships.
|
||||
|
||||
Unlike an NSArray, RLMArrays hold a single type, specified by the `objectClassName` property.
|
||||
This is referred to in these docs as the “type” of the array.
|
||||
|
||||
When declaring an RLMArray property, the type must be marked as conforming to a
|
||||
protocol by the same name as the objects it should contain (see the
|
||||
`RLM_ARRAY_TYPE` macro). RLMArray properties can also use Objective-C generics
|
||||
if available. For example:
|
||||
|
||||
RLM_ARRAY_TYPE(ObjectType)
|
||||
...
|
||||
@property RLMArray<ObjectType *><ObjectType> *arrayOfObjectTypes;
|
||||
|
||||
RLMArrays can be queried with the same predicates as RLMObject and RLMResults.
|
||||
|
||||
RLMArrays cannot be created directly. RLMArray properties on RLMObjects are
|
||||
lazily created when accessed, or can be obtained by querying a Realm.
|
||||
|
||||
### Key-Value Observing
|
||||
|
||||
RLMArray supports array key-value observing on RLMArray properties on RLMObject
|
||||
subclasses, and the `invalidated` property on RLMArray instances themselves is
|
||||
key-value observing compliant when the RLMArray is attached to a persisted
|
||||
RLMObject (RLMArrays on standalone RLMObjects will never become invalidated).
|
||||
|
||||
Because RLMArrays are attached to the object which they are a property of, they
|
||||
do not require using the mutable collection proxy objects from
|
||||
`-mutableArrayValueForKey:` or KVC-compatible mutation methods on the containing
|
||||
object. Instead, you can call the mutation methods on the RLMArray directly.
|
||||
*/
|
||||
|
||||
@interface RLMArray RLM_GENERIC_COLLECTION : NSObject<RLMCollection, NSFastEnumeration>
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
/**
|
||||
Number of objects in the array.
|
||||
*/
|
||||
@property (nonatomic, readonly, assign) NSUInteger count;
|
||||
|
||||
/**
|
||||
The class name (i.e. type) of the RLMObjects contained in this RLMArray.
|
||||
*/
|
||||
@property (nonatomic, readonly, copy) NSString *objectClassName;
|
||||
|
||||
/**
|
||||
The Realm in which this array is persisted. Returns nil for standalone arrays.
|
||||
*/
|
||||
@property (nonatomic, readonly, nullable) RLMRealm *realm;
|
||||
|
||||
/**
|
||||
Indicates if an array can no longer be accessed.
|
||||
*/
|
||||
@property (nonatomic, readonly, getter = isInvalidated) BOOL invalidated;
|
||||
|
||||
#pragma mark - Accessing Objects from an Array
|
||||
|
||||
/**
|
||||
Returns the object at the index specified.
|
||||
|
||||
@param index The index to look up.
|
||||
|
||||
@return An RLMObject of the type contained in this RLMArray.
|
||||
*/
|
||||
- (RLMObjectType)objectAtIndex:(NSUInteger)index;
|
||||
|
||||
/**
|
||||
Returns the first object in the array.
|
||||
|
||||
Returns `nil` if called on an empty RLMArray.
|
||||
|
||||
@return An RLMObject of the type contained in this RLMArray.
|
||||
*/
|
||||
- (nullable RLMObjectType)firstObject;
|
||||
|
||||
/**
|
||||
Returns the last object in the array.
|
||||
|
||||
Returns `nil` if called on an empty RLMArray.
|
||||
|
||||
@return An RLMObject of the type contained in this RLMArray.
|
||||
*/
|
||||
- (nullable RLMObjectType)lastObject;
|
||||
|
||||
|
||||
|
||||
#pragma mark - Adding, Removing, and Replacing Objects in an Array
|
||||
|
||||
/**
|
||||
Adds an object to the end of the array.
|
||||
|
||||
@warning This method can only be called during a write transaction.
|
||||
|
||||
@param object An RLMObject of the type contained in this RLMArray.
|
||||
*/
|
||||
- (void)addObject:(RLMObjectArgument)object;
|
||||
|
||||
/**
|
||||
Adds an array of objects at the end of the array.
|
||||
|
||||
@warning This method can only be called during a write transaction.
|
||||
|
||||
@param objects An enumerable object such as NSArray or RLMResults which contains objects of the
|
||||
same class as this RLMArray.
|
||||
*/
|
||||
- (void)addObjects:(id<NSFastEnumeration>)objects;
|
||||
|
||||
/**
|
||||
Inserts an object at the given index.
|
||||
|
||||
Throws an exception when the index exceeds the bounds of this RLMArray.
|
||||
|
||||
@warning This method can only be called during a write transaction.
|
||||
|
||||
@param anObject An RLMObject of the type contained in this RLMArray.
|
||||
@param index The array index at which the object is inserted.
|
||||
*/
|
||||
- (void)insertObject:(RLMObjectArgument)anObject atIndex:(NSUInteger)index;
|
||||
|
||||
/**
|
||||
Removes an object at a given index.
|
||||
|
||||
Throws an exception when the index exceeds the bounds of this RLMArray.
|
||||
|
||||
@warning This method can only be called during a write transaction.
|
||||
|
||||
@param index The array index identifying the object to be removed.
|
||||
*/
|
||||
- (void)removeObjectAtIndex:(NSUInteger)index;
|
||||
|
||||
/**
|
||||
Removes the last object in an RLMArray.
|
||||
|
||||
@warning This method can only be called during a write transaction.
|
||||
*/
|
||||
- (void)removeLastObject;
|
||||
|
||||
/**
|
||||
Removes all objects from an RLMArray.
|
||||
|
||||
@warning This method can only be called during a write transaction.
|
||||
*/
|
||||
- (void)removeAllObjects;
|
||||
|
||||
/**
|
||||
Replaces an object at the given index with a new object.
|
||||
|
||||
Throws an exception when the index exceeds the bounds of this RLMArray.
|
||||
|
||||
@warning This method can only be called during a write transaction.
|
||||
|
||||
@param index The array index of the object to be replaced.
|
||||
@param anObject An object (of the same type as returned from the objectClassName selector).
|
||||
*/
|
||||
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(RLMObjectArgument)anObject;
|
||||
|
||||
/**
|
||||
Moves the object at the given source index to the given destination index.
|
||||
|
||||
Throws an exception when the index exceeds the bounds of this RLMArray.
|
||||
|
||||
@warning This method can only be called during a write transaction.
|
||||
|
||||
@param sourceIndex The index of the object to be moved.
|
||||
@param destinationIndex The index to which the object at `sourceIndex` should be moved.
|
||||
*/
|
||||
- (void)moveObjectAtIndex:(NSUInteger)sourceIndex toIndex:(NSUInteger)destinationIndex;
|
||||
|
||||
/**
|
||||
Exchanges the objects in the array at given indexes.
|
||||
|
||||
Throws an exception when either index exceeds the bounds of this RLMArray.
|
||||
|
||||
@warning This method can only be called during a write transaction.
|
||||
|
||||
@param index1 The index of the object with which to replace the object at index `index2`.
|
||||
@param index2 The index of the object with which to replace the object at index `index1`.
|
||||
*/
|
||||
- (void)exchangeObjectAtIndex:(NSUInteger)index1 withObjectAtIndex:(NSUInteger)index2;
|
||||
|
||||
#pragma mark - Querying an Array
|
||||
|
||||
/**
|
||||
Gets the index of an object.
|
||||
|
||||
Returns NSNotFound if the object is not found in this RLMArray.
|
||||
|
||||
@param object An object (of the same type as returned from the objectClassName selector).
|
||||
*/
|
||||
- (NSUInteger)indexOfObject:(RLMObjectArgument)object;
|
||||
|
||||
/**
|
||||
Gets the index of the first object matching the predicate.
|
||||
|
||||
@param predicateFormat The predicate format string which can accept variable arguments.
|
||||
|
||||
@return Index of object or NSNotFound if the object is not found in this RLMArray.
|
||||
*/
|
||||
- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat, ...;
|
||||
|
||||
/// :nodoc:
|
||||
- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat args:(va_list)args;
|
||||
|
||||
/**
|
||||
Gets the index of the first object matching the predicate.
|
||||
|
||||
@param predicate The predicate to filter the objects.
|
||||
|
||||
@return Index of object or NSNotFound if the object is not found in this RLMArray.
|
||||
*/
|
||||
- (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate;
|
||||
|
||||
/**
|
||||
Get objects matching the given predicate in the RLMArray.
|
||||
|
||||
@param predicateFormat The predicate format string which can accept variable arguments.
|
||||
|
||||
@return An RLMResults of objects that match the given predicate
|
||||
*/
|
||||
- (RLMResults RLM_GENERIC_RETURN*)objectsWhere:(NSString *)predicateFormat, ...;
|
||||
|
||||
/// :nodoc:
|
||||
- (RLMResults RLM_GENERIC_RETURN*)objectsWhere:(NSString *)predicateFormat args:(va_list)args;
|
||||
|
||||
/**
|
||||
Get objects matching the given predicate in the RLMArray.
|
||||
|
||||
@param predicate The predicate to filter the objects.
|
||||
|
||||
@return An RLMResults of objects that match the given predicate
|
||||
*/
|
||||
- (RLMResults RLM_GENERIC_RETURN*)objectsWithPredicate:(NSPredicate *)predicate;
|
||||
|
||||
/**
|
||||
Get a sorted RLMResults from an RLMArray
|
||||
|
||||
@param property The property name to sort by.
|
||||
@param ascending The direction to sort by.
|
||||
|
||||
@return An RLMResults sorted by the specified property.
|
||||
*/
|
||||
- (RLMResults RLM_GENERIC_RETURN*)sortedResultsUsingProperty:(NSString *)property ascending:(BOOL)ascending;
|
||||
|
||||
/**
|
||||
Get a sorted RLMResults from an RLMArray
|
||||
|
||||
@param properties An array of `RLMSortDescriptor`s to sort by.
|
||||
|
||||
@return An RLMResults sorted by the specified properties.
|
||||
*/
|
||||
- (RLMResults RLM_GENERIC_RETURN*)sortedResultsUsingDescriptors:(NSArray *)properties;
|
||||
|
||||
/// :nodoc:
|
||||
- (RLMObjectType)objectAtIndexedSubscript:(NSUInteger)index;
|
||||
|
||||
/// :nodoc:
|
||||
- (void)setObject:(RLMObjectType)newValue atIndexedSubscript:(NSUInteger)index;
|
||||
|
||||
#pragma mark - Notifications
|
||||
|
||||
/**
|
||||
Register a block to be called each time the RLMArray changes.
|
||||
|
||||
The block will be asynchronously called with the initial array, and then
|
||||
called again after each write transaction which changes the array or any
|
||||
items contained in the array. You must retain the returned token for as long as
|
||||
you want the block to continue to be called. To stop receiving updates, call
|
||||
`-stop` on the token.
|
||||
|
||||
The error parameter will always be `nil`, and is present only for compatiblity
|
||||
with the RLMResults version of this method, which can potentially fail.
|
||||
|
||||
@param block The block to be called each time the array changes.
|
||||
@return A token which must be held for as long as you want notifications to be delivered.
|
||||
*/
|
||||
- (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMArray RLM_GENERIC_RETURN *array, NSError *))block RLM_WARN_UNUSED_RESULT;
|
||||
|
||||
#pragma mark - Unavailable Methods
|
||||
|
||||
/**
|
||||
-[RLMArray init] is not available because RLMArrays cannot be created directly.
|
||||
RLMArray properties on RLMObjects are lazily created when accessed, or can be obtained by querying a Realm.
|
||||
*/
|
||||
- (instancetype)init __attribute__((unavailable("RLMArrays cannot be created directly")));
|
||||
|
||||
/**
|
||||
+[RLMArray new] is not available because RLMArrays cannot be created directly.
|
||||
RLMArray properties on RLMObjects are lazily created when accessed, or can be obtained by querying a Realm.
|
||||
*/
|
||||
+ (instancetype)new __attribute__((unavailable("RLMArrays cannot be created directly")));
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
An RLMSortDescriptor stores a property name and a sort order for use with
|
||||
`sortedResultsUsingDescriptors:`. It is similar to NSSortDescriptor, but supports
|
||||
only the subset of functionality which can be efficiently run by the query
|
||||
engine. RLMSortDescriptor instances are immutable.
|
||||
*/
|
||||
@interface RLMSortDescriptor : NSObject
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
/**
|
||||
The name of the property which this sort descriptor orders results by.
|
||||
*/
|
||||
@property (nonatomic, readonly) NSString *property;
|
||||
|
||||
/**
|
||||
Whether this descriptor sorts in ascending or descending order.
|
||||
*/
|
||||
@property (nonatomic, readonly) BOOL ascending;
|
||||
|
||||
#pragma mark - Methods
|
||||
|
||||
/**
|
||||
Returns a new sort descriptor for the given property name and order.
|
||||
*/
|
||||
+ (instancetype)sortDescriptorWithProperty:(NSString *)propertyName ascending:(BOOL)ascending;
|
||||
|
||||
/**
|
||||
Returns a copy of the receiver with the sort order reversed.
|
||||
*/
|
||||
- (instancetype)reversedSortDescriptor;
|
||||
|
||||
@end
|
||||
|
||||
/// :nodoc:
|
||||
@interface RLMArray (Swift)
|
||||
// for use only in Swift class definitions
|
||||
- (instancetype)initWithObjectClassName:(NSString *)objectClassName;
|
||||
@end
|
||||
|
||||
RLM_ASSUME_NONNULL_END
|
||||
24
Example/Pods/Realm/include/Realm/RLMArray_Private.h
generated
Normal file
24
Example/Pods/Realm/include/Realm/RLMArray_Private.h
generated
Normal file
@@ -0,0 +1,24 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <Realm/RLMArray.h>
|
||||
|
||||
@interface RLMArray ()
|
||||
- (instancetype)initWithObjectClassName:(NSString *)objectClassName;
|
||||
- (NSString *)descriptionWithMaxDepth:(NSUInteger)depth;
|
||||
@end
|
||||
184
Example/Pods/Realm/include/Realm/RLMCollection.h
generated
Normal file
184
Example/Pods/Realm/include/Realm/RLMCollection.h
generated
Normal file
@@ -0,0 +1,184 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <Realm/RLMDefines.h>
|
||||
|
||||
RLM_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class RLMRealm, RLMResults, RLMObject, RLMSortDescriptor, RLMNotificationToken;
|
||||
|
||||
/**
|
||||
A homogenous collection of `RLMObject`s like `RLMArray` or `RLMResults`.
|
||||
*/
|
||||
@protocol RLMCollection <NSFastEnumeration>
|
||||
|
||||
@required
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
/**
|
||||
Number of objects in the collection.
|
||||
*/
|
||||
@property (nonatomic, readonly, assign) NSUInteger count;
|
||||
|
||||
/**
|
||||
The class name (i.e. type) of the RLMObjects contained in this RLMCollection.
|
||||
*/
|
||||
@property (nonatomic, readonly, copy) NSString *objectClassName;
|
||||
|
||||
/**
|
||||
The Realm in which this collection is persisted. Returns nil for standalone collections.
|
||||
*/
|
||||
@property (nonatomic, readonly) RLMRealm *realm;
|
||||
|
||||
#pragma mark - Accessing Objects from a Collection
|
||||
|
||||
/**
|
||||
Returns the object at the index specified.
|
||||
|
||||
@param index The index to look up.
|
||||
|
||||
@return An RLMObject of the type contained in this RLMCollection.
|
||||
*/
|
||||
- (id)objectAtIndex:(NSUInteger)index;
|
||||
|
||||
/**
|
||||
Returns the first object in the collection.
|
||||
|
||||
Returns `nil` if called on an empty RLMCollection.
|
||||
|
||||
@return An RLMObject of the type contained in this RLMCollection.
|
||||
*/
|
||||
- (nullable id)firstObject;
|
||||
|
||||
/**
|
||||
Returns the last object in the collection.
|
||||
|
||||
Returns `nil` if called on an empty RLMCollection.
|
||||
|
||||
@return An RLMObject of the type contained in this RLMCollection.
|
||||
*/
|
||||
- (nullable id)lastObject;
|
||||
|
||||
#pragma mark - Querying a Collection
|
||||
|
||||
/**
|
||||
Gets the index of an object.
|
||||
|
||||
Returns NSNotFound if the object is not found in this RLMCollection.
|
||||
|
||||
@param object An object (of the same type as returned from the objectClassName selector).
|
||||
*/
|
||||
- (NSUInteger)indexOfObject:(RLMObject *)object;
|
||||
|
||||
/**
|
||||
Gets the index of the first object matching the predicate.
|
||||
|
||||
@param predicateFormat The predicate format string which can accept variable arguments.
|
||||
|
||||
@return Index of object or NSNotFound if the object is not found in this RLMCollection.
|
||||
*/
|
||||
- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat, ...;
|
||||
|
||||
/// :nodoc:
|
||||
- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat args:(va_list)args;
|
||||
|
||||
/**
|
||||
Gets the index of the first object matching the predicate.
|
||||
|
||||
@param predicate The predicate to filter the objects.
|
||||
|
||||
@return Index of object or NSNotFound if the object is not found in this RLMCollection.
|
||||
*/
|
||||
- (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate;
|
||||
|
||||
/**
|
||||
Get objects matching the given predicate in the RLMCollection.
|
||||
|
||||
@param predicateFormat The predicate format string which can accept variable arguments.
|
||||
|
||||
@return An RLMResults of objects that match the given predicate
|
||||
*/
|
||||
- (RLMResults *)objectsWhere:(NSString *)predicateFormat, ...;
|
||||
|
||||
/// :nodoc:
|
||||
- (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args;
|
||||
|
||||
/**
|
||||
Get objects matching the given predicate in the RLMCollection.
|
||||
|
||||
@param predicate The predicate to filter the objects.
|
||||
|
||||
@return An RLMResults of objects that match the given predicate
|
||||
*/
|
||||
- (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate;
|
||||
|
||||
/**
|
||||
Get a sorted RLMResults from an RLMCollection.
|
||||
|
||||
@param property The property name to sort by.
|
||||
@param ascending The direction to sort by.
|
||||
|
||||
@return An RLMResults sorted by the specified property.
|
||||
*/
|
||||
- (RLMResults *)sortedResultsUsingProperty:(NSString *)property ascending:(BOOL)ascending;
|
||||
|
||||
/**
|
||||
Get a sorted RLMResults from an RLMCollection.
|
||||
|
||||
@param properties An array of `RLMSortDescriptor`s to sort by.
|
||||
|
||||
@return An RLMResults sorted by the specified properties.
|
||||
*/
|
||||
- (RLMResults *)sortedResultsUsingDescriptors:(NSArray RLM_GENERIC(RLMSortDescriptor *) *)properties;
|
||||
|
||||
/// :nodoc:
|
||||
- (id)objectAtIndexedSubscript:(NSUInteger)index;
|
||||
|
||||
/**
|
||||
Returns an NSArray containing the results of invoking `valueForKey:` using key on each of the collection's objects.
|
||||
|
||||
@param key The name of the property.
|
||||
|
||||
@return NSArray containing the results of invoking `valueForKey:` using key on each of the collection's objects.
|
||||
*/
|
||||
- (nullable id)valueForKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
Invokes `setValue:forKey:` on each of the collection's objects using the specified value and key.
|
||||
|
||||
@warning This method can only be called during a write transaction.
|
||||
|
||||
@param value The object value.
|
||||
@param key The name of the property.
|
||||
*/
|
||||
- (void)setValue:(nullable id)value forKey:(NSString *)key;
|
||||
|
||||
#pragma mark - Notifications
|
||||
|
||||
/**
|
||||
Register a block to be called each time the collection changes.
|
||||
|
||||
@param block The block to be called each time the collection changes.
|
||||
@return A token which must be held for as long as you want notifications to be delivered.
|
||||
*/
|
||||
- (RLMNotificationToken *)addNotificationBlock:(void (^)(id<RLMCollection> collection))block RLM_WARN_UNUSED_RESULT;
|
||||
|
||||
@end
|
||||
|
||||
RLM_ASSUME_NONNULL_END
|
||||
131
Example/Pods/Realm/include/Realm/RLMConstants.h
generated
Normal file
131
Example/Pods/Realm/include/Realm/RLMConstants.h
generated
Normal file
@@ -0,0 +1,131 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <Foundation/Foundation.h>
|
||||
|
||||
#pragma mark - Enums
|
||||
|
||||
/**
|
||||
Property types supported in Realm models.
|
||||
|
||||
See [Realm Models](https://realm.io/docs/objc/latest/#models)
|
||||
*/
|
||||
// Make sure numbers match those in <realm/data_type.hpp>
|
||||
typedef NS_ENUM(int32_t, RLMPropertyType) {
|
||||
|
||||
#pragma mark - Primitive types
|
||||
|
||||
/** Integer type: NSInteger, int, long, Int (Swift) */
|
||||
RLMPropertyTypeInt = 0,
|
||||
/** Boolean type: BOOL, bool, Bool (Swift) */
|
||||
RLMPropertyTypeBool = 1,
|
||||
/** Float type: float, Float (Swift) */
|
||||
RLMPropertyTypeFloat = 9,
|
||||
/** Double type: double, Double (Swift) */
|
||||
RLMPropertyTypeDouble = 10,
|
||||
|
||||
#pragma mark - Object types
|
||||
|
||||
/** String type: NSString, String (Swift) */
|
||||
RLMPropertyTypeString = 2,
|
||||
/** Data type: NSData */
|
||||
RLMPropertyTypeData = 4,
|
||||
/** Any type: id, **not supported in Swift** */
|
||||
RLMPropertyTypeAny = 6,
|
||||
/** Date type: NSDate */
|
||||
RLMPropertyTypeDate = 7,
|
||||
|
||||
#pragma mark - Array/Linked object types
|
||||
|
||||
/** Object type. See [Realm Models](https://realm.io/docs/objc/latest/#models) */
|
||||
RLMPropertyTypeObject = 12,
|
||||
/** Array type. See [Realm Models](http://realms.io/docs/objc/latest/#models) */
|
||||
RLMPropertyTypeArray = 13,
|
||||
};
|
||||
|
||||
/**
|
||||
Enum representing all recoverable errors in Realm.
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, RLMError) {
|
||||
/** Returned by RLMRealm if no other specific error is returned when a realm is opened. */
|
||||
RLMErrorFail = 1,
|
||||
/** Returned by RLMRealm for any I/O related exception scenarios when a realm is opened. */
|
||||
RLMErrorFileAccess = 2,
|
||||
/** Returned by RLMRealm if the user does not have permission to open or create
|
||||
the specified file in the specified access mode when the realm is opened. */
|
||||
RLMErrorFilePermissionDenied = 3,
|
||||
/** Returned by RLMRealm if the file already exists when a copy should be written. */
|
||||
RLMErrorFileExists = 4,
|
||||
/** Returned by RLMRealm if no file was found when a realm was opened as
|
||||
read-only or if the directory part of the specified path was not
|
||||
found when a copy should be written. */
|
||||
RLMErrorFileNotFound = 5,
|
||||
/** Returned by RLMRealm if a file format upgrade is required to open the file, but upgrades were explicilty disabled. */
|
||||
RLMErrorFileFormatUpgradeRequired = 6,
|
||||
/** Returned by RLMRealm if the database file is currently open in another
|
||||
process which cannot share with the current process due to an
|
||||
architecture mismatch. */
|
||||
RLMErrorIncompatibleLockFile = 8,
|
||||
};
|
||||
|
||||
#pragma mark - Constants
|
||||
|
||||
#pragma mark - Notification Constants
|
||||
|
||||
/**
|
||||
Posted by RLMRealm when the data in the realm has changed.
|
||||
|
||||
DidChange are posted after a realm has been refreshed to reflect a write
|
||||
transaction, i.e. when an autorefresh occurs, `[RLMRealm refresh]` is
|
||||
called, after an implicit refresh from `[RLMRealm beginWriteTransaction]`,
|
||||
and after a local write transaction is committed.
|
||||
*/
|
||||
extern NSString * const RLMRealmRefreshRequiredNotification;
|
||||
|
||||
/**
|
||||
Posted by RLMRealm when a write transaction has been committed to an RLMRealm on
|
||||
a different thread for the same file. This is not posted if
|
||||
`[RLMRealm autorefresh]` is enabled or if the RLMRealm is
|
||||
refreshed before the notifcation has a chance to run.
|
||||
|
||||
Realms with autorefresh disabled should normally have a handler for this
|
||||
notification which calls `[RLMRealm refresh]` after doing some work.
|
||||
While not refreshing is allowed, it may lead to large Realm files as Realm has
|
||||
to keep an extra copy of the data for the un-refreshed RLMRealm.
|
||||
*/
|
||||
extern NSString * const RLMRealmDidChangeNotification;
|
||||
|
||||
#pragma mark - Other Constants
|
||||
|
||||
/** Schema version used for uninitialized Realms */
|
||||
extern const uint64_t RLMNotVersioned;
|
||||
|
||||
/** Error domain used in Realm. */
|
||||
extern NSString * const RLMErrorDomain;
|
||||
|
||||
/** Key for name of Realm exceptions. */
|
||||
extern NSString * const RLMExceptionName;
|
||||
|
||||
/** Key for Realm file version. */
|
||||
extern NSString * const RLMRealmVersionKey;
|
||||
|
||||
/** Key for Realm core version. */
|
||||
extern NSString * const RLMRealmCoreVersionKey;
|
||||
|
||||
/** Key for Realm invalidated property name. */
|
||||
extern NSString * const RLMInvalidatedKey;
|
||||
95
Example/Pods/Realm/include/Realm/RLMDefines.h
generated
Normal file
95
Example/Pods/Realm/include/Realm/RLMDefines.h
generated
Normal file
@@ -0,0 +1,95 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2015 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 <Foundation/Foundation.h>
|
||||
|
||||
@class RLMObject;
|
||||
|
||||
#ifndef __has_feature
|
||||
#define __has_feature(x) 0
|
||||
#endif
|
||||
|
||||
#pragma mark - Generics
|
||||
|
||||
#if __has_extension(objc_generics)
|
||||
#define RLM_GENERIC(...) <__VA_ARGS__>
|
||||
#define RLM_GENERIC_COLLECTION <RLMObjectType: RLMObject *>
|
||||
#define RLM_GENERIC_RETURN <RLMObjectType>
|
||||
#define RLMObjectArgument RLMObjectType
|
||||
#else
|
||||
#define RLM_GENERIC(...)
|
||||
#define RLM_GENERIC_COLLECTION
|
||||
#define RLM_GENERIC_RETURN
|
||||
typedef id RLMObjectType;
|
||||
typedef RLMObject * RLMObjectArgument;
|
||||
#endif
|
||||
|
||||
#pragma mark - Nullability
|
||||
|
||||
#if !__has_feature(nullability)
|
||||
#ifndef __nullable
|
||||
#define __nullable
|
||||
#endif
|
||||
#ifndef __nonnull
|
||||
#define __nonnull
|
||||
#endif
|
||||
#ifndef __null_unspecified
|
||||
#define __null_unspecified
|
||||
#endif
|
||||
#ifndef nullable
|
||||
#define nullable
|
||||
#endif
|
||||
#ifndef nonnull
|
||||
#define nonnull
|
||||
#endif
|
||||
#ifndef null_unspecified
|
||||
#define null_unspecified
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(NS_ASSUME_NONNULL_BEGIN) && defined(NS_ASSUME_NONNULL_END)
|
||||
#define RLM_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
|
||||
#define RLM_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
|
||||
#else
|
||||
#define RLM_ASSUME_NONNULL_BEGIN
|
||||
#define RLM_ASSUME_NONNULL_END
|
||||
#endif
|
||||
|
||||
#pragma mark - Escaping
|
||||
|
||||
#if __has_attribute(noescape)
|
||||
# define RLM_NOESCAPE __attribute__((noescape))
|
||||
#else
|
||||
# define RLM_NOESCAPE
|
||||
#endif
|
||||
|
||||
#pragma mark - Unused Result
|
||||
|
||||
#if __has_attribute(warn_unused_result)
|
||||
# define RLM_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
|
||||
#else
|
||||
# define RLM_WARN_UNUSED_RESULT
|
||||
#endif
|
||||
|
||||
#pragma mark - Swift Availability
|
||||
|
||||
#if defined(NS_SWIFT_UNAVAILABLE)
|
||||
# define RLM_SWIFT_UNAVAILABLE(msg) NS_SWIFT_UNAVAILABLE(msg)
|
||||
#else
|
||||
# define RLM_SWIFT_UNAVAILABLE(msg)
|
||||
#endif
|
||||
29
Example/Pods/Realm/include/Realm/RLMListBase.h
generated
Normal file
29
Example/Pods/Realm/include/Realm/RLMListBase.h
generated
Normal file
@@ -0,0 +1,29 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <Foundation/Foundation.h>
|
||||
|
||||
@class RLMArray;
|
||||
|
||||
// A base class for Swift generic Lists to make it possible to interact with
|
||||
// them from obj-c
|
||||
@interface RLMListBase : NSObject <NSFastEnumeration>
|
||||
@property (nonatomic, strong) RLMArray *_rlmArray;
|
||||
|
||||
- (instancetype)initWithArray:(RLMArray *)array;
|
||||
@end
|
||||
107
Example/Pods/Realm/include/Realm/RLMMigration.h
generated
Normal file
107
Example/Pods/Realm/include/Realm/RLMMigration.h
generated
Normal file
@@ -0,0 +1,107 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <Foundation/Foundation.h>
|
||||
#import <Realm/RLMDefines.h>
|
||||
|
||||
RLM_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class RLMSchema;
|
||||
@class RLMArray;
|
||||
@class RLMObject;
|
||||
|
||||
/**
|
||||
Provides both the old and new versions of an object in this Realm. Object properties can only be
|
||||
accessed using keyed subscripting.
|
||||
|
||||
@param oldObject Object in original RLMRealm (read-only).
|
||||
@param newObject Object in migrated RLMRealm (read-write).
|
||||
*/
|
||||
typedef void (^RLMObjectMigrationBlock)(RLMObject * __nullable oldObject, RLMObject * __nullable newObject);
|
||||
|
||||
/**
|
||||
RLMMigration is the object passed into a user defined RLMMigrationBlock when updating the version
|
||||
of an RLMRealm instance.
|
||||
|
||||
This object provides access to the RLMSchema current to this migration.
|
||||
*/
|
||||
@interface RLMMigration : NSObject
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
/**
|
||||
Get the old RLMSchema for the migration. This is the schema which describes the RLMRealm before the
|
||||
migration is applied.
|
||||
*/
|
||||
@property (nonatomic, readonly) RLMSchema *oldSchema;
|
||||
|
||||
/**
|
||||
Get the new RLMSchema for the migration. This is the schema which describes the RLMRealm after applying
|
||||
a migration.
|
||||
*/
|
||||
@property (nonatomic, readonly) RLMSchema *newSchema;
|
||||
|
||||
|
||||
#pragma mark - Altering Objects during a Migration
|
||||
|
||||
/**
|
||||
Enumerates objects of a given type in this Realm, providing both the old and new versions of each object.
|
||||
Objects properties can be accessed using keyed subscripting.
|
||||
|
||||
@param className The name of the RLMObject class to enumerate.
|
||||
|
||||
@warning All objects returned are of a type specific to the current migration and should not be casted
|
||||
to className. Instead you should access them as RLMObjects and use keyed subscripting to access
|
||||
properties.
|
||||
*/
|
||||
- (void)enumerateObjects:(NSString *)className block:(RLMObjectMigrationBlock)block;
|
||||
|
||||
/**
|
||||
Create an RLMObject of type `className` in the Realm being migrated.
|
||||
|
||||
@param className The name of the RLMObject class to create.
|
||||
@param value The value used to populate the created object. This can be any key/value coding compliant
|
||||
object, or a JSON object such as those returned from the methods in NSJSONSerialization, or
|
||||
an NSArray with one object for each persisted property. An exception will be
|
||||
thrown if any required properties are not present and no default is set.
|
||||
|
||||
When passing in an NSArray, all properties must be present, valid and in the same order as the properties defined in the model.
|
||||
*/
|
||||
-(RLMObject *)createObject:(NSString *)className withValue:(id)value;
|
||||
|
||||
/**
|
||||
Delete an object from a Realm during a migration. This can be called within `enumerateObjects:block:`.
|
||||
|
||||
@param object Object to be deleted from the Realm being migrated.
|
||||
*/
|
||||
- (void)deleteObject:(RLMObject *)object;
|
||||
|
||||
/**
|
||||
Deletes the data for the class with the given name.
|
||||
This deletes all objects of the given class, and if the RLMObject subclass no longer exists in your program,
|
||||
cleans up any remaining metadata for the class in the Realm file.
|
||||
|
||||
@param name The name of the RLMObject class to delete.
|
||||
|
||||
@return whether there was any data to delete.
|
||||
*/
|
||||
- (BOOL)deleteDataForClassName:(NSString *)name;
|
||||
|
||||
@end
|
||||
|
||||
RLM_ASSUME_NONNULL_END
|
||||
34
Example/Pods/Realm/include/Realm/RLMMigration_Private.h
generated
Normal file
34
Example/Pods/Realm/include/Realm/RLMMigration_Private.h
generated
Normal file
@@ -0,0 +1,34 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <Realm/RLMMigration.h>
|
||||
#import <Realm/RLMObjectBase.h>
|
||||
#import <Realm/RLMRealm.h>
|
||||
|
||||
typedef void (^RLMObjectBaseMigrationBlock)(RLMObjectBase *oldObject, RLMObjectBase *newObject);
|
||||
|
||||
@interface RLMMigration ()
|
||||
|
||||
@property (nonatomic, strong) RLMRealm *oldRealm;
|
||||
@property (nonatomic, strong) RLMRealm *realm;
|
||||
|
||||
- (instancetype)initWithRealm:(RLMRealm *)realm oldRealm:(RLMRealm *)oldRealm;
|
||||
|
||||
- (void)execute:(RLMMigrationBlock)block;
|
||||
|
||||
@end
|
||||
425
Example/Pods/Realm/include/Realm/RLMObject.h
generated
Normal file
425
Example/Pods/Realm/include/Realm/RLMObject.h
generated
Normal file
@@ -0,0 +1,425 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <Foundation/Foundation.h>
|
||||
|
||||
#import <Realm/RLMObjectBase.h>
|
||||
|
||||
RLM_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class RLMRealm;
|
||||
@class RLMResults;
|
||||
@class RLMObjectSchema;
|
||||
|
||||
/**
|
||||
|
||||
In Realm you define your model classes by subclassing `RLMObject` and adding properties to be persisted.
|
||||
You then instantiate and use your custom subclasses instead of using the `RLMObject` class directly.
|
||||
|
||||
// Dog.h
|
||||
@interface Dog : RLMObject
|
||||
@property NSString *name;
|
||||
@property BOOL adopted;
|
||||
@end
|
||||
|
||||
// Dog.m
|
||||
@implementation Dog
|
||||
@end //none needed
|
||||
|
||||
### Supported property types
|
||||
|
||||
- `NSString`
|
||||
- `NSInteger`, `int`, `long`, `float`, and `double`
|
||||
- `BOOL` or `bool`
|
||||
- `NSDate`
|
||||
- `NSData`
|
||||
- `NSNumber<X>`, where X is one of RLMInt, RLMFloat, RLMDouble or RLMBool, for optional number properties
|
||||
- `RLMObject` subclasses, so you can have many-to-one relationships.
|
||||
- `RLMArray<X>`, where X is an `RLMObject` subclass, so you can have many-to-many relationships.
|
||||
|
||||
### Querying
|
||||
|
||||
You can query an object directly via the class methods: `allObjects`, `objectsWhere:`, and `objectsWithPredicate:`.
|
||||
These methods allow you to easily query a custom subclass for instances of this class in the
|
||||
default Realm. To search in a Realm other than the default Realm use the interface on an RLMRealm instance.
|
||||
|
||||
### Relationships
|
||||
|
||||
See our [Cocoa guide](https://realm.io/docs/objc/latest#relationships) for more details.
|
||||
|
||||
### Key-Value Observing
|
||||
|
||||
All `RLMObject` properties (including properties you create in subclasses) are
|
||||
[Key-Value Observing compliant](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html),
|
||||
except for `realm` and `objectSchema`. There are several Realm-specific things
|
||||
to keep in mind when observing Realm objects:
|
||||
|
||||
1. Unlike `NSMutableArray` properties, `RLMArray` properties do not require
|
||||
using the proxy object returned from `-mutableArrayValueForKey:`, or defining
|
||||
KVC mutation methods on the containing class. You can simply call methods on
|
||||
the RLMArray directly and the changes will be observed by the containing
|
||||
object.
|
||||
2. Standalone `RLMObjects` cannot be added to a Realm while they have any
|
||||
observed properties.
|
||||
3. Modifying persisted `RLMObjects` in `-observeValueForKeyPath:ofObject:change:context:`
|
||||
is problematic. Properties may change when the Realm is not in a write
|
||||
transaction (for example, when `-[RLMRealm refresh]` is called after changes
|
||||
are made on a different thread), and notifications sent prior to the change
|
||||
being applied (when `NSKeyValueObservingOptionPrior` is used) may be sent at
|
||||
times when you *cannot* begin a write transaction.
|
||||
*/
|
||||
|
||||
@interface RLMObject : RLMObjectBase
|
||||
|
||||
#pragma mark - Creating & Initializing Objects
|
||||
|
||||
/**
|
||||
Initialize a standalone RLMObject
|
||||
|
||||
Initialize an unpersisted instance of this object.
|
||||
Call addObject: on an RLMRealm to add standalone object to a realm.
|
||||
|
||||
@see [RLMRealm addObject:]:
|
||||
*/
|
||||
- (instancetype)init NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
|
||||
/**
|
||||
Initialize a standalone RLMObject with values from an NSArray or NSDictionary
|
||||
|
||||
Initialize an unpersisted instance of this object.
|
||||
Call addObject: on an RLMRealm to add standalone object to a realm.
|
||||
|
||||
@see [RLMRealm addObject:]:
|
||||
*/
|
||||
- (instancetype)initWithValue:(id)value NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
|
||||
/**
|
||||
Helper to return the class name for an RLMObject subclass.
|
||||
|
||||
@warning Do not override. Realm relies on this method returning the exact class
|
||||
name.
|
||||
|
||||
@return The class name for the model class.
|
||||
*/
|
||||
+ (NSString *)className;
|
||||
|
||||
/**
|
||||
Create an RLMObject in the default Realm with a given value.
|
||||
|
||||
Creates an instance of this object and adds it to the default Realm populating
|
||||
the object with the given value.
|
||||
|
||||
If nested objects are included in the argument, `createInDefaultRealmWithValue:` will be called
|
||||
on them.
|
||||
|
||||
@param value The value used to populate the object. This can be any key/value coding compliant
|
||||
object, or a JSON object such as those returned from the methods in NSJSONSerialization, or
|
||||
an NSArray with one object for each persisted property. An exception will be
|
||||
thrown if any required properties are not present and no default is set.
|
||||
|
||||
When passing in an NSArray, all properties must be present, valid and in the same order as the properties defined in the model.
|
||||
|
||||
@see defaultPropertyValues
|
||||
*/
|
||||
+ (instancetype)createInDefaultRealmWithValue:(id)value;
|
||||
|
||||
/**
|
||||
Create an RLMObject in a Realm with a given object.
|
||||
|
||||
Creates an instance of this object and adds it to the given Realm populating
|
||||
the object with the given object.
|
||||
|
||||
If nested objects are included in the argument, `createInRealm:withValue:` will be called
|
||||
on them.
|
||||
|
||||
@param realm The Realm in which this object is persisted.
|
||||
@param value The value used to populate the object. This can be any key/value coding compliant
|
||||
object, or a JSON object such as those returned from the methods in NSJSONSerialization, or
|
||||
an NSArray with one object for each persisted property. An exception will be
|
||||
thrown if any required properties are not present and no default is set.
|
||||
|
||||
When passing in an NSArray, all properties must be present, valid and in the same order as the properties defined in the model.
|
||||
|
||||
@see defaultPropertyValues
|
||||
*/
|
||||
+ (instancetype)createInRealm:(RLMRealm *)realm withValue:(id)value;
|
||||
|
||||
/**
|
||||
Create or update an RLMObject in the default Realm with a given object.
|
||||
|
||||
This method can only be called on object types with a primary key defined. If there is already
|
||||
an object with the same primary key value in the default RLMRealm its values are updated and the object
|
||||
is returned. Otherwise this creates and populates a new instance of this object in the default Realm.
|
||||
|
||||
If nested objects are included in the argument, `createOrUpdateInDefaultRealmWithValue:` will be
|
||||
called on them if have a primary key (`createInDefaultRealmWithValue:` otherwise).
|
||||
|
||||
This is a no-op if the argument is an RLMObject of the same type already backed by the target realm.
|
||||
|
||||
@param value The value used to populate the object. This can be any key/value coding compliant
|
||||
object, or a JSON object such as those returned from the methods in NSJSONSerialization, or
|
||||
an NSArray with one object for each persisted property. An exception will be
|
||||
thrown if any required properties are not present and no default is set.
|
||||
|
||||
When passing in an NSArray, all properties must be present, valid and in the same order as the properties defined in the model.
|
||||
|
||||
@see defaultPropertyValues, primaryKey
|
||||
*/
|
||||
+ (instancetype)createOrUpdateInDefaultRealmWithValue:(id)value;
|
||||
|
||||
/**
|
||||
Create or update an RLMObject with a given object.
|
||||
|
||||
This method can only be called on object types with a primary key defined. If there is already
|
||||
an object with the same primary key value in the provided RLMRealm its values are updated and the object
|
||||
is returned. Otherwise this creates and populates a new instance of this object in the provided Realm.
|
||||
|
||||
If nested objects are included in the argument, `createOrUpdateInRealm:withValue:` will be
|
||||
called on them if have a primary key (`createInRealm:withValue:` otherwise).
|
||||
|
||||
This is a no-op if the argument is an RLMObject of the same type already backed by the target realm.
|
||||
|
||||
@param realm The Realm in which this object is persisted.
|
||||
@param value The value used to populate the object. This can be any key/value coding compliant
|
||||
object, or a JSON object such as those returned from the methods in NSJSONSerialization, or
|
||||
an NSArray with one object for each persisted property. An exception will be
|
||||
thrown if any required properties are not present and no default is set.
|
||||
|
||||
When passing in an NSArray, all properties must be present, valid and in the same order as the properties defined in the model.
|
||||
|
||||
@see defaultPropertyValues, primaryKey
|
||||
*/
|
||||
+ (instancetype)createOrUpdateInRealm:(RLMRealm *)realm withValue:(id)value;
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
/**
|
||||
The Realm in which this object is persisted. Returns nil for standalone objects.
|
||||
*/
|
||||
@property (nonatomic, readonly, nullable) RLMRealm *realm;
|
||||
|
||||
/**
|
||||
The ObjectSchema which lists the persisted properties for this object.
|
||||
*/
|
||||
@property (nonatomic, readonly) RLMObjectSchema *objectSchema;
|
||||
|
||||
/**
|
||||
Indicates if an object can no longer be accessed.
|
||||
|
||||
An object can no longer be accessed if the object has been deleted from the containing `realm` or
|
||||
if `invalidate` is called on the containing `realm`.
|
||||
*/
|
||||
@property (nonatomic, readonly, getter = isInvalidated) BOOL invalidated;
|
||||
|
||||
|
||||
#pragma mark - Customizing your Objects
|
||||
|
||||
/**
|
||||
Return an array of property names for properties which should be indexed. Only supported
|
||||
for strings, integers, booleans and NSDate properties.
|
||||
@return NSArray of property names.
|
||||
*/
|
||||
+ (NSArray RLM_GENERIC(NSString *) *)indexedProperties;
|
||||
|
||||
/**
|
||||
Implement to indicate the default values to be used for each property.
|
||||
|
||||
@return NSDictionary mapping property names to their default values.
|
||||
*/
|
||||
+ (nullable NSDictionary *)defaultPropertyValues;
|
||||
|
||||
/**
|
||||
Implement to designate a property as the primary key for an RLMObject subclass. Only properties of
|
||||
type RLMPropertyTypeString and RLMPropertyTypeInt can be designated as the primary key. Primary key
|
||||
properties enforce uniqueness for each value whenever the property is set which incurs some overhead.
|
||||
Indexes are created automatically for primary key properties.
|
||||
|
||||
@return Name of the property designated as the primary key.
|
||||
*/
|
||||
+ (nullable NSString *)primaryKey;
|
||||
|
||||
/**
|
||||
Implement to return an array of property names to ignore. These properties will not be persisted
|
||||
and are treated as transient.
|
||||
|
||||
@return NSArray of property names to ignore.
|
||||
*/
|
||||
+ (nullable NSArray RLM_GENERIC(NSString *) *)ignoredProperties;
|
||||
|
||||
/**
|
||||
Implement to return an array of property names that should not allow storing nil.
|
||||
|
||||
By default, all properties of a type that support storing nil are considered optional properties.
|
||||
To require that an object in a Realm always have a non-nil value for a property,
|
||||
add the name of the property to the array returned from this method.
|
||||
|
||||
Currently Object properties cannot be required. Array and NSNumber properties
|
||||
can, but it makes little sense to do so: arrays do not support storing nil, and
|
||||
if you want a non-optional number you should instead use the primitive type.
|
||||
|
||||
@return NSArray of property names that are required.
|
||||
*/
|
||||
+ (NSArray RLM_GENERIC(NSString *) *)requiredProperties;
|
||||
|
||||
|
||||
#pragma mark - Getting & Querying Objects from the Default Realm
|
||||
|
||||
/**
|
||||
Get all objects of this type from the default Realm.
|
||||
|
||||
@return An RLMResults of all objects of this type in the default Realm.
|
||||
*/
|
||||
+ (RLMResults *)allObjects;
|
||||
|
||||
/**
|
||||
Get objects matching the given predicate for this type from the default Realm.
|
||||
|
||||
@param predicateFormat The predicate format string which can accept variable arguments.
|
||||
|
||||
@return An RLMResults of objects of the subclass type in the default Realm that match the given predicate
|
||||
*/
|
||||
+ (RLMResults *)objectsWhere:(NSString *)predicateFormat, ...;
|
||||
|
||||
/// :nodoc:
|
||||
+ (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args;
|
||||
|
||||
|
||||
/**
|
||||
Get objects matching the given predicate for this type from the default Realm.
|
||||
|
||||
@param predicate The predicate to filter the objects.
|
||||
|
||||
@return An RLMResults of objects of the subclass type in the default Realm that match the given predicate
|
||||
*/
|
||||
+ (RLMResults *)objectsWithPredicate:(nullable NSPredicate *)predicate;
|
||||
|
||||
/**
|
||||
Get the single object with the given primary key from the default Realm.
|
||||
|
||||
Returns the object from the default Realm which has the given primary key, or
|
||||
`nil` if the object does not exist. This is slightly faster than the otherwise
|
||||
equivalent `[[SubclassName objectsWhere:@"primaryKeyPropertyName = %@", key] firstObject]`.
|
||||
|
||||
This method requires that `primaryKey` be overridden on the receiving subclass.
|
||||
|
||||
@return An object of the subclass type or nil if an object with the given primary key does not exist.
|
||||
@see -primaryKey
|
||||
*/
|
||||
+ (nullable instancetype)objectForPrimaryKey:(nullable id)primaryKey;
|
||||
|
||||
|
||||
#pragma mark - Querying Specific Realms
|
||||
|
||||
/**
|
||||
Get all objects of this type from the specified Realm.
|
||||
|
||||
@param realm The Realm instance to query.
|
||||
|
||||
@return An RLMResults of all objects of this type in the specified Realm.
|
||||
*/
|
||||
+ (RLMResults *)allObjectsInRealm:(RLMRealm *)realm;
|
||||
|
||||
/**
|
||||
Get objects matching the given predicate for this type from the specified Realm.
|
||||
|
||||
@param predicateFormat The predicate format string which can accept variable arguments.
|
||||
@param realm The Realm instance to query.
|
||||
|
||||
@return An RLMResults of objects of the subclass type in the specified Realm that match the given predicate
|
||||
*/
|
||||
+ (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat, ...;
|
||||
|
||||
/// :nodoc:
|
||||
+ (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat args:(va_list)args;
|
||||
|
||||
/**
|
||||
Get objects matching the given predicate for this type from the specified Realm.
|
||||
|
||||
@param predicate The predicate to filter the objects.
|
||||
@param realm The Realm instance to query.
|
||||
|
||||
@return An RLMResults of objects of the subclass type in the specified Realm that match the given predicate
|
||||
*/
|
||||
+ (RLMResults *)objectsInRealm:(RLMRealm *)realm withPredicate:(nullable NSPredicate *)predicate;
|
||||
|
||||
/**
|
||||
Get the single object with the given primary key from the specified Realm.
|
||||
|
||||
Returns the object from the specified Realm which has the given primary key, or
|
||||
`nil` if the object does not exist. This is slightly faster than the otherwise
|
||||
equivalent `[[SubclassName objectsInRealm:realm where:@"primaryKeyPropertyName = %@", key] firstObject]`.
|
||||
|
||||
This method requires that `primaryKey` be overridden on the receiving subclass.
|
||||
|
||||
@return An object of the subclass type or nil if an object with the given primary key does not exist.
|
||||
@see -primaryKey
|
||||
*/
|
||||
+ (nullable instancetype)objectInRealm:(RLMRealm *)realm forPrimaryKey:(nullable id)primaryKey;
|
||||
|
||||
#pragma mark - Other Instance Methods
|
||||
|
||||
/**
|
||||
Get an `NSArray` of objects of type `className` which have this object as the given property value. This can
|
||||
be used to get the inverse relationship value for `RLMObject` and `RLMArray` properties.
|
||||
|
||||
@param className The type of object on which the relationship to query is defined.
|
||||
@param property The name of the property which defines the relationship.
|
||||
|
||||
@return An NSArray of objects of type `className` which have this object as their value for the `property` property.
|
||||
*/
|
||||
- (NSArray *)linkingObjectsOfClass:(NSString *)className forProperty:(NSString *)property;
|
||||
|
||||
/**
|
||||
Returns YES if another RLMObject points to the same object in an RLMRealm. For RLMObject types
|
||||
with a primary, key, `isEqual:` is overridden to use this method (along with a corresponding
|
||||
implementation for `hash`.
|
||||
|
||||
@param object The object to compare to.
|
||||
|
||||
@return YES if the object represents the same object in the same RLMRealm.
|
||||
*/
|
||||
- (BOOL)isEqualToObject:(RLMObject *)object;
|
||||
|
||||
#pragma mark - Dynamic Accessors
|
||||
|
||||
/// :nodoc:
|
||||
- (nullable id)objectForKeyedSubscript:(NSString *)key;
|
||||
|
||||
/// :nodoc:
|
||||
- (void)setObject:(nullable id)obj forKeyedSubscript:(NSString *)key;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - RLMArray Property Declaration
|
||||
|
||||
/**
|
||||
Properties on RLMObjects of type RLMArray must have an associated type. A type is associated
|
||||
with an RLMArray property by defining a protocol for the object type which the RLMArray will
|
||||
hold. To define the protocol for an object you can use the macro RLM_ARRAY_TYPE:
|
||||
|
||||
RLM_ARRAY_TYPE(ObjectType)
|
||||
...
|
||||
@property RLMArray<ObjectType *><ObjectType> *arrayOfObjectTypes;
|
||||
*/
|
||||
#define RLM_ARRAY_TYPE(RLM_OBJECT_SUBCLASS)\
|
||||
@protocol RLM_OBJECT_SUBCLASS <NSObject> \
|
||||
@end
|
||||
|
||||
RLM_ASSUME_NONNULL_END
|
||||
42
Example/Pods/Realm/include/Realm/RLMObjectBase.h
generated
Normal file
42
Example/Pods/Realm/include/Realm/RLMObjectBase.h
generated
Normal file
@@ -0,0 +1,42 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <Foundation/Foundation.h>
|
||||
#import <Realm/RLMDefines.h>
|
||||
|
||||
RLM_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class RLMRealm;
|
||||
@class RLMSchema;
|
||||
@class RLMObjectSchema;
|
||||
|
||||
/// :nodoc:
|
||||
@interface RLMObjectBase : NSObject
|
||||
|
||||
@property (nonatomic, readonly, getter = isInvalidated) BOOL invalidated;
|
||||
|
||||
- (instancetype)init NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
+ (NSString *)className;
|
||||
|
||||
// Returns whether the class is included in the default set of classes persisted in a Realm.
|
||||
+ (BOOL)shouldIncludeInDefaultSchema;
|
||||
|
||||
@end
|
||||
|
||||
RLM_ASSUME_NONNULL_END
|
||||
84
Example/Pods/Realm/include/Realm/RLMObjectBase_Dynamic.h
generated
Normal file
84
Example/Pods/Realm/include/Realm/RLMObjectBase_Dynamic.h
generated
Normal file
@@ -0,0 +1,84 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <Realm/RLMObject.h>
|
||||
|
||||
@class RLMObjectSchema, RLMRealm;
|
||||
|
||||
/**
|
||||
This function is useful only in specialized circumstances, for example, when building components
|
||||
that integrate with Realm. If you are simply building an app on Realm, it is
|
||||
recommended to retrieve `realm` via `RLMObject`.
|
||||
|
||||
@param object an RLMObjectBase obtained via a Swift Object or RLMObject
|
||||
|
||||
@return The Realm in which this object is persisted. Returns nil for standalone objects.
|
||||
*/
|
||||
FOUNDATION_EXTERN RLMRealm *RLMObjectBaseRealm(RLMObjectBase *object);
|
||||
|
||||
/**
|
||||
This function is useful only in specialized circumstances, for example, when building components
|
||||
that integrate with Realm. If you are simply building an app on Realm, it is
|
||||
recommended to retrieve `objectSchema` via `RLMObject`.
|
||||
|
||||
@param object an RLMObjectBase obtained via a Swift Object or RLMObject
|
||||
|
||||
@return The ObjectSchema which lists the persisted properties for this object.
|
||||
*/
|
||||
FOUNDATION_EXTERN RLMObjectSchema *RLMObjectBaseObjectSchema(RLMObjectBase *object);
|
||||
|
||||
/**
|
||||
This function is useful only in specialized circumstances, for example, when building components
|
||||
that integrate with Realm. If you are simply building an app on Realm, it is
|
||||
recommended to retrieve the linking objects via `RLMObject`.
|
||||
|
||||
@param object an RLMObjectBase obtained via a Swift Object or RLMObject
|
||||
@param className The type of object on which the relationship to query is defined.
|
||||
@param property The name of the property which defines the relationship.
|
||||
|
||||
@return An NSArray of objects of type `className` which have this object as their value for the `property` property.
|
||||
*/
|
||||
FOUNDATION_EXTERN NSArray *RLMObjectBaseLinkingObjectsOfClass(RLMObjectBase *object, NSString *className, NSString *property);
|
||||
|
||||
/**
|
||||
This function is useful only in specialized circumstances, for example, when building components
|
||||
that integrate with Realm. If you are simply building an app on Realm, it is
|
||||
recommended to retrieve key values via `RLMObject`.
|
||||
|
||||
@warning Will throw `NSUndefinedKeyException` if key is not present on the object
|
||||
|
||||
@param object an RLMObjectBase obtained via a Swift Object or RLMObject
|
||||
@param key The name of the property
|
||||
|
||||
@return the object for the property requested
|
||||
*/
|
||||
FOUNDATION_EXTERN id RLMObjectBaseObjectForKeyedSubscript(RLMObjectBase *object, NSString *key);
|
||||
|
||||
/**
|
||||
This function is useful only in specialized circumstances, for example, when building components
|
||||
that integrate with Realm. If you are simply building an app on Realm, it is
|
||||
recommended to set key values via `RLMObject`.
|
||||
|
||||
@warning Will throw `NSUndefinedKeyException` if key is not present on the object
|
||||
|
||||
@param object an RLMObjectBase obtained via a Swift Object or RLMObject
|
||||
@param key The name of the property
|
||||
@param obj The object to set as the value of the key
|
||||
*/
|
||||
FOUNDATION_EXTERN void RLMObjectBaseSetObjectForKeyedSubscript(RLMObjectBase *object, NSString *key, id obj);
|
||||
|
||||
73
Example/Pods/Realm/include/Realm/RLMObjectSchema.h
generated
Normal file
73
Example/Pods/Realm/include/Realm/RLMObjectSchema.h
generated
Normal file
@@ -0,0 +1,73 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <Foundation/Foundation.h>
|
||||
#import <Realm/RLMDefines.h>
|
||||
|
||||
RLM_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class RLMProperty;
|
||||
|
||||
/**
|
||||
This class represents Realm model object schemas.
|
||||
|
||||
When using Realm, RLMObjectSchema objects allow performing migrations and
|
||||
introspecting the database's schema.
|
||||
|
||||
Object schemas map to tables in the core database.
|
||||
*/
|
||||
@interface RLMObjectSchema : NSObject<NSCopying>
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
/**
|
||||
Array of persisted RLMProperty objects for an object.
|
||||
|
||||
@see RLMProperty
|
||||
*/
|
||||
@property (nonatomic, readonly, copy) NSArray RLM_GENERIC(RLMProperty *) *properties;
|
||||
|
||||
/**
|
||||
The name of the class this schema describes.
|
||||
*/
|
||||
@property (nonatomic, readonly) NSString *className;
|
||||
|
||||
/**
|
||||
The property which is the primary key for this object (if any).
|
||||
*/
|
||||
@property (nonatomic, readonly, nullable) RLMProperty *primaryKeyProperty;
|
||||
|
||||
#pragma mark - Methods
|
||||
|
||||
/**
|
||||
Retrieve an RLMProperty object by name.
|
||||
|
||||
@param propertyName The property's name.
|
||||
|
||||
@return RLMProperty object or nil if there is no property with the given name.
|
||||
*/
|
||||
- (nullable RLMProperty *)objectForKeyedSubscript:(id <NSCopying>)propertyName;
|
||||
|
||||
/**
|
||||
Returns YES if equal to objectSchema
|
||||
*/
|
||||
- (BOOL)isEqualToObjectSchema:(RLMObjectSchema *)objectSchema;
|
||||
|
||||
@end
|
||||
|
||||
RLM_ASSUME_NONNULL_END
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user