// 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 #import #include "base/compiler_specific.h" #include "base/logging.h" #include "base/memory/ref_counted.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): // scoped_nsobject foo([[Foo alloc] init]); // WeakNSObject 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 { public: explicit WeakContainer(id object); id object() { DCHECK(checker_.CalledOnValidThread()); return object_; } void nullify() { DCHECK(checker_.CalledOnValidThread()); object_ = nil; } private: friend base::RefCountedThreadSafe; ~WeakContainer(); base::ThreadChecker checker_; __unsafe_unretained 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)containerForObject:(id)object; @end namespace base { // Base class for all WeakNSObject derivatives. template class WeakNSProtocol { public: explicit WeakNSProtocol(NST object = nil) { container_ = [CRBWeakNSProtocolSentinel containerForObject:object]; } WeakNSProtocol(const WeakNSProtocol& 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& 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 container_; base::ThreadChecker checker_; }; // Free functions template bool operator==(NST p1, const WeakNSProtocol& p2) { return p1 == p2.get(); } template bool operator!=(NST p1, const WeakNSProtocol& p2) { return p1 != p2.get(); } template class WeakNSObject : public WeakNSProtocol { public: explicit WeakNSObject(NST* object = nil) : WeakNSProtocol(object) {} WeakNSObject(const WeakNSObject& that) : WeakNSProtocol(that) {} WeakNSObject& operator=(const WeakNSObject& that) { WeakNSProtocol::operator=(that); return *this; } }; // Specialization to make WeakNSObject work. template <> class WeakNSObject : public WeakNSProtocol { public: explicit WeakNSObject(id object = nil) : WeakNSProtocol(object) {} WeakNSObject(const WeakNSObject& that) : WeakNSProtocol(that) {} WeakNSObject& operator=(const WeakNSObject& that) { WeakNSProtocol::operator=(that); return *this; } }; } // namespace base #endif // BASE_IOS_WEAK_NSOBJECT_H_