1 // Copyright 2013 The Chromium 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.
5 #ifndef BASE_IOS_WEAK_NSOBJECT_H_
6 #define BASE_IOS_WEAK_NSOBJECT_H_
8 #import <Foundation/Foundation.h>
9 #import <objc/runtime.h>
11 #include "base/basictypes.h"
12 #include "base/compiler_specific.h"
13 #include "base/logging.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/threading/non_thread_safe.h"
16 #include "base/threading/thread_checker.h"
18 // WeakNSObject<> is patterned after scoped_nsobject<>, but instead of
19 // maintaining ownership of an NSObject subclass object, it will nil itself out
20 // when the object is deallocated.
22 // WeakNSProtocol<> has the same behavior as WeakNSObject, but can be used
25 // Example usage (base::WeakNSObject<T>):
26 // scoped_nsobject<Foo> foo([[Foo alloc] init]);
27 // WeakNSObject<Foo> weak_foo; // No pointer
28 // weak_foo.reset(foo) // Now a weak reference is kept.
29 // [weak_foo description]; // Returns [foo description].
30 // foo.reset(); // The reference is released.
31 // [weak_foo description]; // Returns nil, as weak_foo is pointing to nil.
34 // Implementation wise a WeakNSObject keeps a reference to a refcounted
35 // WeakContainer. There is one unique instance of a WeakContainer per watched
36 // NSObject, this relationship is maintained via the ObjectiveC associated
37 // object API, indirectly via an ObjectiveC CRBWeakNSProtocolSentinel class.
39 // Threading restrictions:
40 // - Several WeakNSObject pointing to the same underlying object must all be
41 // created and dereferenced on the same thread;
42 // - thread safety is enforced by the implementation, except in two cases:
43 // (1) it is allowed to copy a WeakNSObject on a different thread. However,
44 // that copy must return to the original thread before being dereferenced,
45 // (2) it is allowed to destroy a WeakNSObject on any thread;
46 // - the implementation assumes that the tracked object will be released on the
47 // same thread that the WeakNSObject is created on.
50 // WeakContainer keeps a weak pointer to an object and clears it when it
51 // receives nullify() from the object's sentinel.
52 class WeakContainer
: public base::RefCountedThreadSafe
<WeakContainer
> {
54 explicit WeakContainer(id object
) : object_(object
) {}
57 DCHECK(checker_
.CalledOnValidThread());
62 DCHECK(checker_
.CalledOnValidThread());
67 friend base::RefCountedThreadSafe
<WeakContainer
>;
69 base::ThreadChecker checker_
;
75 // Sentinel for observing the object contained in the weak pointer. The object
76 // will be deleted when the weak object is deleted and will notify its
78 @interface CRBWeakNSProtocolSentinel
: NSObject
79 // Return the only associated container for this object. There can be only one.
80 // Will return null if object is nil .
81 + (scoped_refptr
<base::WeakContainer
>)containerForObject
:(id
)object
;
86 // Base class for all WeakNSObject derivatives.
87 template <typename NST
>
88 class WeakNSProtocol
{
90 explicit WeakNSProtocol(NST object
= nil
) {
91 container_
= [CRBWeakNSProtocolSentinel containerForObject
:object
];
94 WeakNSProtocol(const WeakNSProtocol
<NST
>& that
) {
95 // A WeakNSProtocol object can be copied on one thread and used on
97 checker_
.DetachFromThread();
98 container_
= that
.container_
;
102 // A WeakNSProtocol object can be used on one thread and released on
103 // another. This is not the case for the contained object.
104 checker_
.DetachFromThread();
107 void reset(NST object
= nil
) {
108 DCHECK(checker_
.CalledOnValidThread());
109 container_
= [CRBWeakNSProtocolSentinel containerForObject
:object
];
113 DCHECK(checker_
.CalledOnValidThread());
114 if (!container_
.get())
116 return container_
->object();
119 WeakNSProtocol
& operator=(const WeakNSProtocol
<NST
>& that
) {
120 DCHECK(checker_
.CalledOnValidThread());
121 container_
= that
.container_
;
125 bool operator==(NST that
) const {
126 DCHECK(checker_
.CalledOnValidThread());
127 return get() == that
;
130 bool operator!=(NST that
) const {
131 DCHECK(checker_
.CalledOnValidThread());
132 return get() != that
;
135 operator NST() const {
136 DCHECK(checker_
.CalledOnValidThread());
141 // Refecounted reference to the container tracking the ObjectiveC object this
142 // class encapsulates.
143 scoped_refptr
<base::WeakContainer
> container_
;
144 base::ThreadChecker checker_
;
149 bool operator==(NST p1
, const WeakNSProtocol
<NST
>& p2
) {
150 return p1
== p2
.get();
154 bool operator!=(NST p1
, const WeakNSProtocol
<NST
>& p2
) {
155 return p1
!= p2
.get();
158 template <typename NST
>
159 class WeakNSObject
: public WeakNSProtocol
<NST
*> {
161 explicit WeakNSObject(NST
* object
= nil
) : WeakNSProtocol
<NST
*>(object
) {}
163 WeakNSObject(const WeakNSObject
<NST
>& that
) : WeakNSProtocol
<NST
*>(that
) {}
165 WeakNSObject
& operator=(const WeakNSObject
<NST
>& that
) {
166 WeakNSProtocol
<NST
*>::operator=(that
);
171 // Specialization to make WeakNSObject<id> work.
173 class WeakNSObject
<id
> : public WeakNSProtocol
<id
> {
175 explicit WeakNSObject(id object
= nil
) : WeakNSProtocol
<id
>(object
) {}
177 WeakNSObject(const WeakNSObject
<id
>& that
) : WeakNSProtocol
<id
>(that
) {}
179 WeakNSObject
& operator=(const WeakNSObject
<id
>& that
) {
180 WeakNSProtocol
<id
>::operator=(that
);
187 #endif // BASE_IOS_WEAK_NSOBJECT_H_