Flutter Engine
The Flutter Engine
weak_nsobject.h
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef FLUTTER_FML_PLATFORM_DARWIN_WEAK_NSOBJECT_H_
6#define FLUTTER_FML_PLATFORM_DARWIN_WEAK_NSOBJECT_H_
7
8#import <Foundation/Foundation.h>
9#import <objc/runtime.h>
10
11#include <stdlib.h>
12
13#include <utility>
14#include "flutter/fml/compiler_specific.h"
15#include "flutter/fml/logging.h"
16#include "flutter/fml/memory/ref_counted.h"
17#include "flutter/fml/memory/ref_ptr.h"
18#include "flutter/fml/memory/thread_checker.h"
19
20namespace debug {
23};
24} // namespace debug
25
26// WeakNSObject<> is patterned after scoped_nsobject<>, but instead of
27// maintaining ownership of an NSObject subclass object, it will nil itself out
28// when the object is deallocated.
29//
30// WeakNSProtocol<> has the same behavior as WeakNSObject, but can be used
31// with protocols.
32//
33// Example usage (fml::WeakNSObject<T>):
34// WeakNSObjectFactory factory([[Foo alloc] init]);
35// WeakNSObject<Foo> weak_foo; // No pointer
36// weak_foo = factory.GetWeakNSObject() // Now a weak reference is kept.
37// [weak_foo description]; // Returns [foo description].
38// foo.reset(); // The reference is released.
39// [weak_foo description]; // Returns nil, as weak_foo is pointing to nil.
40//
41//
42// Implementation wise a WeakNSObject keeps a reference to a refcounted
43// WeakContainer. There is one unique instance of a WeakContainer per watched
44// NSObject, this relationship is maintained via the ObjectiveC associated
45// object API, indirectly via an ObjectiveC CRBWeakNSProtocolSentinel class.
46//
47// Threading restrictions:
48// - Several WeakNSObject pointing to the same underlying object must all be
49// created and dereferenced on the same thread;
50// - thread safety is enforced by the implementation, except:
51// - it is allowed to destroy a WeakNSObject on any thread;
52// - the implementation assumes that the tracked object will be released on the
53// same thread that the WeakNSObject is created on.
54//
55// fml specifics:
56// WeakNSObjects can only originate from a |WeakNSObjectFactory| (see below), though WeakNSObjects
57// are copyable and movable.
58//
59// WeakNSObjects are not in general thread-safe. They may only be *used* on
60// a single thread, namely the same thread as the "originating"
61// |WeakNSObjectFactory| (which can invalidate the WeakNSObjects that it
62// generates).
63//
64// However, WeakNSObject may be passed to other threads, reset on other
65// threads, or destroyed on other threads. They may also be reassigned on
66// other threads (in which case they should then only be used on the thread
67// corresponding to the new "originating" |WeakNSObjectFactory|).
68namespace fml {
69
70// Forward declaration, so |WeakNSObject<NST>| can friend it.
71template <typename NST>
72class WeakNSObjectFactory;
73
74// WeakContainer keeps a weak pointer to an object and clears it when it
75// receives nullify() from the object's sentinel.
76class WeakContainer : public fml::RefCountedThreadSafe<WeakContainer> {
77 public:
78 explicit WeakContainer(id object, const debug::DebugThreadChecker& checker);
79
80 id object() {
81 CheckThreadSafety();
82 return object_;
83 }
84
85 void nullify() { object_ = nil; }
86
87 private:
90
91 __unsafe_unretained id object_;
92
93 void CheckThreadSafety() const { FML_DCHECK_CREATION_THREAD_IS_CURRENT(checker_.checker); }
94
95// checker_ is unused in non-unopt mode.
96#pragma clang diagnostic push
97#pragma clang diagnostic ignored "-Wunused-private-field"
99#pragma clang diagnostic pop
100};
101
102} // namespace fml
103
104// Sentinel for observing the object contained in the weak pointer. The object
105// will be deleted when the weak object is deleted and will notify its
106// container.
107@interface CRBWeakNSProtocolSentinel : NSObject
108// Return the only associated container for this object. There can be only one.
109// Will return null if object is nil .
110+ (fml::RefPtr<fml::WeakContainer>)containerForObject:(id)object
111 threadChecker:(debug::DebugThreadChecker)checker;
112@end
113
114namespace fml {
115
116// Base class for all WeakNSObject derivatives.
117template <typename NST>
119 public:
120 WeakNSProtocol() = default;
121
122 // A WeakNSProtocol object can be copied on one thread and used on
123 // another.
125 : container_(that.container_), checker_(that.checker_) {}
126
127 ~WeakNSProtocol() = default;
128
129 void reset() {
131 }
132
133 NST get() const {
135 if (!container_.get()) {
136 return nil;
137 }
138 return container_->object();
139 }
140
142 // A WeakNSProtocol object can be copied on one thread and used on
143 // another.
144 container_ = that.container_;
145 checker_ = that.checker_;
146 return *this;
147 }
148
149 bool operator==(NST that) const {
151 return get() == that;
152 }
153
154 bool operator!=(NST that) const {
156 return get() != that;
157 }
158
159 // This appears to be intentional to allow truthiness?
160 // NOLINTNEXTLINE(google-explicit-constructor)
161 operator NST() const {
163 return get();
164 }
165
166 protected:
167 friend class WeakNSObjectFactory<NST>;
168
170 const debug::DebugThreadChecker& checker)
171 : container_(std::move(container)), checker_(checker) {}
172
173 // Refecounted reference to the container tracking the ObjectiveC object this
174 // class encapsulates.
177
179};
180
181// Free functions
182template <class NST>
183bool operator==(NST p1, const WeakNSProtocol<NST>& p2) {
184 return p1 == p2.get();
185}
186
187template <class NST>
188bool operator!=(NST p1, const WeakNSProtocol<NST>& p2) {
189 return p1 != p2.get();
190}
191
192template <typename NST>
193class WeakNSObject : public WeakNSProtocol<NST*> {
194 public:
195 WeakNSObject() = default;
196 WeakNSObject(const WeakNSObject<NST>& that) : WeakNSProtocol<NST*>(that) {}
197
200 return *this;
201 }
202
203 private:
204 friend class WeakNSObjectFactory<NST>;
205
207 : WeakNSProtocol<NST*>(container, checker) {}
208};
209
210// Specialization to make WeakNSObject<id> work.
211template <>
213 public:
214 WeakNSObject() = default;
216
219 return *this;
220 }
221
222 private:
223 friend class WeakNSObjectFactory<id>;
224
225 explicit WeakNSObject(const RefPtr<fml::WeakContainer>& container,
226 const debug::DebugThreadChecker& checker)
227 : WeakNSProtocol<id>(container, checker) {}
228};
229
230// Class that produces (valid) |WeakNSObject<NST>|s. Typically, this is used as a
231// member variable of |NST| (preferably the last one -- see below), and |NST|'s
232// methods control how WeakNSObjects to it are vended. This class is not
233// thread-safe, and should only be created, destroyed and used on a single
234// thread.
235//
236// Example:
237//
238// ```objc
239// @implementation Controller {
240// std::unique_ptr<fml::WeakNSObjectFactory<Controller>> _weakFactory;
241// }
242//
243// - (instancetype)init {
244// self = [super init];
245// _weakFactory = std::make_unique<fml::WeakNSObjectFactory<Controller>>(self)
246// }
247
248// - (fml::WeakNSObject<Controller>) {
249// return _weakFactory->GetWeakNSObject()
250// }
251//
252// @end
253// ```
254template <typename NST>
256 public:
257 explicit WeakNSObjectFactory(NST* object) {
258 FML_DCHECK(object);
259 container_ = [CRBWeakNSProtocolSentinel containerForObject:object threadChecker:checker_];
260 }
261
262 ~WeakNSObjectFactory() { CheckThreadSafety(); }
263
264 // Gets a new weak pointer, which will be valid until this object is
265 // destroyed.e
266 WeakNSObject<NST> GetWeakNSObject() const { return WeakNSObject<NST>(container_, checker_); }
267
268 private:
269 // Refecounted reference to the container tracking the ObjectiveC object this
270 // class encapsulates.
272
273 void CheckThreadSafety() const { FML_DCHECK_CREATION_THREAD_IS_CURRENT(checker_.checker); }
274
276
277 FML_DISALLOW_COPY_AND_ASSIGN(WeakNSObjectFactory);
278};
279
280} // namespace fml
281
282#endif // FLUTTER_FML_PLATFORM_DARWIN_WEAK_NSOBJECT_H_
T * get() const
Definition: ref_ptr.h:116
WeakContainer(id object, const debug::DebugThreadChecker &checker)
WeakNSObjectFactory(NST *object)
WeakNSObject< NST > GetWeakNSObject() const
WeakNSObject(const WeakNSObject< id > &that)
WeakNSObject & operator=(const WeakNSObject< id > &that)
WeakNSObject()=default
WeakNSObject & operator=(const WeakNSObject< NST > &that)
WeakNSObject(const WeakNSObject< NST > &that)
bool operator==(NST that) const
void CheckThreadSafety() const
RefPtr< fml::WeakContainer > container_
WeakNSProtocol(const WeakNSProtocol< NST > &that)
WeakNSProtocol(RefPtr< fml::WeakContainer > container, const debug::DebugThreadChecker &checker)
debug::DebugThreadChecker checker_
bool operator!=(NST that) const
WeakNSProtocol & operator=(const WeakNSProtocol< NST > &that)
#define FML_DCHECK(condition)
Definition: logging.h:103
fml::RefPtr< fml::WeakContainer > containerForObject:threadChecker:(id object,[threadChecker] debug::DebugThreadChecker checker)
Definition: ascii_trie.cc:9
bool operator==(C p1, const scoped_nsprotocol< C > &p2)
bool operator!=(C p1, const scoped_nsprotocol< C > &p2)
Definition: ref_ptr.h:256
const uintptr_t id
#define FML_DCHECK_CREATION_THREAD_IS_CURRENT(c)