summaryrefslogtreecommitdiff
path: root/base/ios/weak_nsobject.h
blob: fc3a7c38dc4ab5b195bdc9669d5ff50bc1095e7f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_IOS_WEAK_NSOBJECT_H_
#define BASE_IOS_WEAK_NSOBJECT_H_

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/threading/non_thread_safe.h"
#include "base/threading/thread_checker.h"

// WeakNSObject<> is patterned after scoped_nsobject<>, but instead of
// maintaining ownership of an NSObject subclass object, it will nil itself out
// when the object is deallocated.
//
// WeakNSProtocol<> has the same behavior as WeakNSObject, but can be used
// with protocols.
//
// Example usage (base::WeakNSObject<T>):
//   scoped_nsobject<Foo> foo([[Foo alloc] init]);
//   WeakNSObject<Foo> weak_foo;  // No pointer
//   weak_foo.reset(foo)  // Now a weak reference is kept.
//   [weak_foo description];  // Returns [foo description].
//   foo.reset();  // The reference is released.
//   [weak_foo description];  // Returns nil, as weak_foo is pointing to nil.
//
//
// Implementation wise a WeakNSObject keeps a reference to a refcounted
// WeakContainer. There is one unique instance of a WeakContainer per watched
// NSObject, this relationship is maintained via the ObjectiveC associated
// object API, indirectly via an ObjectiveC CRBWeakNSProtocolSentinel class.
//
// Threading restrictions:
// - Several WeakNSObject pointing to the same underlying object must all be
//   created and dereferenced on the same thread;
// - thread safety is enforced by the implementation, except in two cases:
//   (1) it is allowed to copy a WeakNSObject on a different thread. However,
//       that copy must return to the original thread before being dereferenced,
//   (2) it is allowed to destroy a WeakNSObject on any thread;
// - the implementation assumes that the tracked object will be released on the
//   same thread that the WeakNSObject is created on.
namespace base {

// WeakContainer keeps a weak pointer to an object and clears it when it
// receives nullify() from the object's sentinel.
class WeakContainer : public base::RefCountedThreadSafe<WeakContainer> {
 public:
  explicit WeakContainer(id object) : object_(object) {}

  id object() {
    DCHECK(checker_.CalledOnValidThread());
    return object_;
  }

  void nullify() {
    DCHECK(checker_.CalledOnValidThread());
    object_ = nil;
  }

 private:
  friend base::RefCountedThreadSafe<WeakContainer>;
  ~WeakContainer() {}
  base::ThreadChecker checker_;
  id object_;
};

}  // namespace base

// Sentinel for observing the object contained in the weak pointer. The object
// will be deleted when the weak object is deleted and will notify its
// container.
@interface CRBWeakNSProtocolSentinel : NSObject
// Return the only associated container for this object. There can be only one.
// Will return null if object is nil .
+ (scoped_refptr<base::WeakContainer>)containerForObject:(id)object;
@end

namespace base {

// Base class for all WeakNSObject derivatives.
template <typename NST>
class WeakNSProtocol {
 public:
  explicit WeakNSProtocol(NST object = nil) {
    container_ = [CRBWeakNSProtocolSentinel containerForObject:object];
  }

  WeakNSProtocol(const WeakNSProtocol<NST>& that) {
    // A WeakNSProtocol object can be copied on one thread and used on
    // another.
    checker_.DetachFromThread();
    container_ = that.container_;
  }

  ~WeakNSProtocol() {
    // A WeakNSProtocol object can be used on one thread and released on
    // another. This is not the case for the contained object.
    checker_.DetachFromThread();
  }

  void reset(NST object = nil) {
    DCHECK(checker_.CalledOnValidThread());
    container_ = [CRBWeakNSProtocolSentinel containerForObject:object];
  }

  NST get() const {
    DCHECK(checker_.CalledOnValidThread());
    if (!container_.get())
      return nil;
    return container_->object();
  }

  WeakNSProtocol& operator=(const WeakNSProtocol<NST>& that) {
    // A WeakNSProtocol object can be copied on one thread and used on
    // another.
    checker_.DetachFromThread();
    container_ = that.container_;
    return *this;
  }

  bool operator==(NST that) const {
    DCHECK(checker_.CalledOnValidThread());
    return get() == that;
  }

  bool operator!=(NST that) const {
    DCHECK(checker_.CalledOnValidThread());
    return get() != that;
  }

  operator NST() const {
    DCHECK(checker_.CalledOnValidThread());
    return get();
  }

 private:
  // Refecounted reference to the container tracking the ObjectiveC object this
  // class encapsulates.
  scoped_refptr<base::WeakContainer> container_;
  base::ThreadChecker checker_;
};

// Free functions
template <class NST>
bool operator==(NST p1, const WeakNSProtocol<NST>& p2) {
  return p1 == p2.get();
}

template <class NST>
bool operator!=(NST p1, const WeakNSProtocol<NST>& p2) {
  return p1 != p2.get();
}

template <typename NST>
class WeakNSObject : public WeakNSProtocol<NST*> {
 public:
  explicit WeakNSObject(NST* object = nil) : WeakNSProtocol<NST*>(object) {}

  WeakNSObject(const WeakNSObject<NST>& that) : WeakNSProtocol<NST*>(that) {}

  WeakNSObject& operator=(const WeakNSObject<NST>& that) {
    WeakNSProtocol<NST*>::operator=(that);
    return *this;
  }
};

// Specialization to make WeakNSObject<id> work.
template <>
class WeakNSObject<id> : public WeakNSProtocol<id> {
 public:
  explicit WeakNSObject(id object = nil) : WeakNSProtocol<id>(object) {}

  WeakNSObject(const WeakNSObject<id>& that) : WeakNSProtocol<id>(that) {}

  WeakNSObject& operator=(const WeakNSObject<id>& that) {
    WeakNSProtocol<id>::operator=(that);
    return *this;
  }
};

}  // namespace base

#endif  // BASE_IOS_WEAK_NSOBJECT_H_