Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit b518b6a

Browse files
authored
Merge branch 'main' into revert-48083-revert_ee590d48afa19b106775c6ee68e99f58760c2637
2 parents 913a711 + b29c564 commit b518b6a

19 files changed

+669
-93
lines changed

ci/licenses_golden/excluded_files

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@
104104
../../../flutter/fml/platform/darwin/scoped_nsobject_arc_unittests.mm
105105
../../../flutter/fml/platform/darwin/scoped_nsobject_unittests.mm
106106
../../../flutter/fml/platform/darwin/string_range_sanitization_unittests.mm
107+
../../../flutter/fml/platform/darwin/weak_nsobject_arc_unittests.mm
108+
../../../flutter/fml/platform/darwin/weak_nsobject_unittests.mm
107109
../../../flutter/fml/platform/win/file_win_unittests.cc
108110
../../../flutter/fml/platform/win/wstring_conversion_unittests.cc
109111
../../../flutter/fml/raster_thread_merger_unittests.cc

ci/licenses_golden/licenses_flutter

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2799,6 +2799,8 @@ ORIGIN: ../../../flutter/fml/platform/darwin/scoped_policy.h + ../../../flutter/
27992799
ORIGIN: ../../../flutter/fml/platform/darwin/scoped_typeref.h + ../../../flutter/LICENSE
28002800
ORIGIN: ../../../flutter/fml/platform/darwin/string_range_sanitization.h + ../../../flutter/LICENSE
28012801
ORIGIN: ../../../flutter/fml/platform/darwin/string_range_sanitization.mm + ../../../flutter/LICENSE
2802+
ORIGIN: ../../../flutter/fml/platform/darwin/weak_nsobject.h + ../../../flutter/LICENSE
2803+
ORIGIN: ../../../flutter/fml/platform/darwin/weak_nsobject.mm + ../../../flutter/LICENSE
28022804
ORIGIN: ../../../flutter/fml/platform/fuchsia/message_loop_fuchsia.cc + ../../../flutter/LICENSE
28032805
ORIGIN: ../../../flutter/fml/platform/fuchsia/message_loop_fuchsia.h + ../../../flutter/LICENSE
28042806
ORIGIN: ../../../flutter/fml/platform/fuchsia/paths_fuchsia.cc + ../../../flutter/LICENSE
@@ -5562,6 +5564,8 @@ FILE: ../../../flutter/fml/platform/darwin/scoped_policy.h
55625564
FILE: ../../../flutter/fml/platform/darwin/scoped_typeref.h
55635565
FILE: ../../../flutter/fml/platform/darwin/string_range_sanitization.h
55645566
FILE: ../../../flutter/fml/platform/darwin/string_range_sanitization.mm
5567+
FILE: ../../../flutter/fml/platform/darwin/weak_nsobject.h
5568+
FILE: ../../../flutter/fml/platform/darwin/weak_nsobject.mm
55655569
FILE: ../../../flutter/fml/platform/fuchsia/message_loop_fuchsia.cc
55665570
FILE: ../../../flutter/fml/platform/fuchsia/message_loop_fuchsia.h
55675571
FILE: ../../../flutter/fml/platform/fuchsia/paths_fuchsia.cc

fml/BUILD.gn

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ source_set("fml") {
169169
"platform/darwin/scoped_typeref.h",
170170
"platform/darwin/string_range_sanitization.h",
171171
"platform/darwin/string_range_sanitization.mm",
172+
"platform/darwin/weak_nsobject.h",
173+
"platform/darwin/weak_nsobject.mm",
172174
]
173175

174176
frameworks = [ "Foundation.framework" ]
@@ -369,7 +371,10 @@ if (enable_unittests) {
369371
}
370372

371373
if (is_mac || is_ios) {
372-
sources += [ "platform/darwin/scoped_nsobject_unittests.mm" ]
374+
sources += [
375+
"platform/darwin/scoped_nsobject_unittests.mm",
376+
"platform/darwin/weak_nsobject_unittests.mm",
377+
]
373378
}
374379

375380
if (is_win) {
@@ -399,7 +404,10 @@ if (enable_unittests) {
399404
testonly = true
400405
if (is_mac || is_ios) {
401406
cflags_objcc = flutter_cflags_objc_arc
402-
sources = [ "platform/darwin/scoped_nsobject_arc_unittests.mm" ]
407+
sources = [
408+
"platform/darwin/scoped_nsobject_arc_unittests.mm",
409+
"platform/darwin/weak_nsobject_arc_unittests.mm",
410+
]
403411
}
404412

405413
deps = [

fml/platform/darwin/weak_nsobject.h

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

0 commit comments

Comments
 (0)