ThreadSafeReference.swift 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2016 Realm Inc.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. //
  17. ////////////////////////////////////////////////////////////////////////////
  18. import Realm
  19. /**
  20. Objects of types which conform to `ThreadConfined` can be managed by a Realm, which will make
  21. them bound to a thread-specific `Realm` instance. Managed objects must be explicitly exported
  22. and imported to be passed between threads.
  23. Managed instances of objects conforming to this protocol can be converted to a thread-safe
  24. reference for transport between threads by passing to the `ThreadSafeReference(to:)` constructor.
  25. Note that only types defined by Realm can meaningfully conform to this protocol, and defining new
  26. classes which attempt to conform to it will not make them work with `ThreadSafeReference`.
  27. */
  28. public protocol ThreadConfined {
  29. // Must also conform to `AssistedObjectiveCBridgeable`
  30. /**
  31. The Realm which manages the object, or `nil` if the object is unmanaged.
  32. Unmanaged objects are not confined to a thread and cannot be passed to methods expecting a
  33. `ThreadConfined` object.
  34. */
  35. var realm: Realm? { get }
  36. /// Indicates if the object can no longer be accessed because it is now invalid.
  37. var isInvalidated: Bool { get }
  38. /**
  39. Indicates if the object is frozen.
  40. Frozen objects are not confined to their source thread. Forming a `ThreadSafeReference` to a
  41. frozen object is allowed, but is unlikely to be useful.
  42. */
  43. var isFrozen: Bool { get }
  44. /**
  45. Returns a frozen snapshot of this object.
  46. Unlike normal Realm live objects, the frozen copy can be read from any thread, and the values
  47. read will never update to reflect new writes to the Realm. Frozen collections can be queried
  48. like any other Realm collection. Frozen objects cannot be mutated, and cannot be observed for
  49. change notifications.
  50. Unmanaged Realm objects cannot be frozen.
  51. - warning: Holding onto a frozen object for an extended period while performing write
  52. transaction on the Realm may result in the Realm file growing to large sizes. See
  53. `Realm.Configuration.maximumNumberOfActiveVersions` for more information.
  54. */
  55. func freeze() -> Self
  56. }
  57. /**
  58. An object intended to be passed between threads containing a thread-safe reference to its
  59. thread-confined object.
  60. To resolve a thread-safe reference on a target Realm on a different thread, pass to
  61. `Realm.resolve(_:)`.
  62. - warning: A `ThreadSafeReference` object must be resolved at most once.
  63. Failing to resolve a `ThreadSafeReference` will result in the source version of the
  64. Realm being pinned until the reference is deallocated.
  65. - note: Prefer short-lived `ThreadSafeReference`s as the data for the version of the source Realm
  66. will be retained until all references have been resolved or deallocated.
  67. - see: `ThreadConfined`
  68. - see: `Realm.resolve(_:)`
  69. */
  70. public struct ThreadSafeReference<Confined: ThreadConfined> {
  71. private let swiftMetadata: Any?
  72. /**
  73. Indicates if the reference can no longer be resolved because an attempt to resolve it has
  74. already occurred. References can only be resolved once.
  75. */
  76. public var isInvalidated: Bool { return objectiveCReference.isInvalidated }
  77. private let objectiveCReference: RLMThreadSafeReference<RLMThreadConfined>
  78. /**
  79. Create a thread-safe reference to the thread-confined object.
  80. - parameter threadConfined: The thread-confined object to create a thread-safe reference to.
  81. - note: You may continue to use and access the thread-confined object after passing it to this
  82. constructor.
  83. */
  84. public init(to threadConfined: Confined) {
  85. let bridged = (threadConfined as! AssistedObjectiveCBridgeable).bridged
  86. swiftMetadata = bridged.metadata
  87. objectiveCReference = RLMThreadSafeReference(threadConfined: bridged.objectiveCValue as! RLMThreadConfined)
  88. }
  89. internal func resolve(in realm: Realm) -> Confined? {
  90. guard let objectiveCValue = realm.rlmRealm.__resolve(objectiveCReference) else { return nil }
  91. return ((Confined.self as! AssistedObjectiveCBridgeable.Type).bridging(from: objectiveCValue, with: swiftMetadata) as! Confined)
  92. }
  93. }
  94. extension Realm {
  95. // MARK: Thread Safe Reference
  96. /**
  97. Returns the same object as the one referenced when the `ThreadSafeReference` was first
  98. created, but resolved for the current Realm for this thread. Returns `nil` if this object was
  99. deleted after the reference was created.
  100. - parameter reference: The thread-safe reference to the thread-confined object to resolve in
  101. this Realm.
  102. - warning: A `ThreadSafeReference` object must be resolved at most once.
  103. Failing to resolve a `ThreadSafeReference` will result in the source version of the
  104. Realm being pinned until the reference is deallocated.
  105. An exception will be thrown if a reference is resolved more than once.
  106. - warning: Cannot call within a write transaction.
  107. - note: Will refresh this Realm if the source Realm was at a later version than this one.
  108. - see: `ThreadSafeReference(to:)`
  109. */
  110. public func resolve<Confined>(_ reference: ThreadSafeReference<Confined>) -> Confined? {
  111. return reference.resolve(in: self)
  112. }
  113. }