ThreadSafeReference.swift 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  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. Returns a live (mutable) reference of this object.
  58. Will return self if called on an already live object.
  59. */
  60. func thaw() -> Self?
  61. }
  62. /**
  63. An object intended to be passed between threads containing a thread-safe reference to its
  64. thread-confined object.
  65. To resolve a thread-safe reference on a target Realm on a different thread, pass to
  66. `Realm.resolve(_:)`.
  67. - warning: A `ThreadSafeReference` object must be resolved at most once.
  68. Failing to resolve a `ThreadSafeReference` will result in the source version of the
  69. Realm being pinned until the reference is deallocated.
  70. - note: Prefer short-lived `ThreadSafeReference`s as the data for the version of the source Realm
  71. will be retained until all references have been resolved or deallocated.
  72. - see: `ThreadConfined`
  73. - see: `Realm.resolve(_:)`
  74. */
  75. @frozen public struct ThreadSafeReference<Confined: ThreadConfined> {
  76. private let swiftMetadata: Any?
  77. /**
  78. Indicates if the reference can no longer be resolved because an attempt to resolve it has
  79. already occurred. References can only be resolved once.
  80. */
  81. public var isInvalidated: Bool { return objectiveCReference.isInvalidated }
  82. private let objectiveCReference: RLMThreadSafeReference<RLMThreadConfined>
  83. /**
  84. Create a thread-safe reference to the thread-confined object.
  85. - parameter threadConfined: The thread-confined object to create a thread-safe reference to.
  86. - note: You may continue to use and access the thread-confined object after passing it to this
  87. constructor.
  88. */
  89. public init(to threadConfined: Confined) {
  90. let bridged = (threadConfined as! AssistedObjectiveCBridgeable).bridged
  91. swiftMetadata = bridged.metadata
  92. objectiveCReference = RLMThreadSafeReference(threadConfined: bridged.objectiveCValue as! RLMThreadConfined)
  93. }
  94. internal func resolve(in realm: Realm) -> Confined? {
  95. guard let objectiveCValue = realm.rlmRealm.__resolve(objectiveCReference) else { return nil }
  96. return ((Confined.self as! AssistedObjectiveCBridgeable.Type).bridging(from: objectiveCValue, with: swiftMetadata) as! Confined)
  97. }
  98. }
  99. extension Realm {
  100. // MARK: Thread Safe Reference
  101. /**
  102. Returns the same object as the one referenced when the `ThreadSafeReference` was first
  103. created, but resolved for the current Realm for this thread. Returns `nil` if this object was
  104. deleted after the reference was created.
  105. - parameter reference: The thread-safe reference to the thread-confined object to resolve in
  106. this Realm.
  107. - warning: A `ThreadSafeReference` object must be resolved at most once.
  108. Failing to resolve a `ThreadSafeReference` will result in the source version of the
  109. Realm being pinned until the reference is deallocated.
  110. An exception will be thrown if a reference is resolved more than once.
  111. - warning: Cannot call within a write transaction.
  112. - note: Will refresh this Realm if the source Realm was at a later version than this one.
  113. - see: `ThreadSafeReference(to:)`
  114. */
  115. public func resolve<Confined>(_ reference: ThreadSafeReference<Confined>) -> Confined? {
  116. return reference.resolve(in: self)
  117. }
  118. }