summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordroger <droger@chromium.org>2014-12-12 09:39:02 -0800
committerCommit bot <commit-bot@chromium.org>2014-12-12 17:39:23 +0000
commitc6762659cdcb0c0d3c2a430ae964ab04429253ca (patch)
treefb100fd13cf5d581401a8c83dce4eecf29698b28
parent7ec6e063e36a66300570ab0adfd88c2e5acc6bd3 (diff)
downloadchromium_src-c6762659cdcb0c0d3c2a430ae964ab04429253ca.zip
chromium_src-c6762659cdcb0c0d3c2a430ae964ab04429253ca.tar.gz
chromium_src-c6762659cdcb0c0d3c2a430ae964ab04429253ca.tar.bz2
Upstream WeakNSObject in //base/ios
Review URL: https://codereview.chromium.org/792443004 Cr-Commit-Position: refs/heads/master@{#308114}
-rw-r--r--base/BUILD.gn3
-rw-r--r--base/base.gyp1
-rw-r--r--base/base.gypi2
-rw-r--r--base/ios/weak_nsobject.h166
-rw-r--r--base/ios/weak_nsobject.mm61
-rw-r--r--base/ios/weak_nsobject_unittest.mm97
6 files changed, 330 insertions, 0 deletions
diff --git a/base/BUILD.gn b/base/BUILD.gn
index a928319..de464f9 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -251,6 +251,8 @@ component("base") {
"ios/ios_util.mm",
"ios/scoped_critical_action.h",
"ios/scoped_critical_action.mm",
+ "ios/weak_nsobject.h",
+ "ios/weak_nsobject.mm",
"json/json_file_value_serializer.cc",
"json/json_file_value_serializer.h",
"json/json_parser.cc",
@@ -1195,6 +1197,7 @@ test("base_unittests") {
"i18n/time_formatting_unittest.cc",
"i18n/timezone_unittest.cc",
"ios/device_util_unittest.mm",
+ "ios/weak_nsobject_unittest.mm",
"json/json_parser_unittest.cc",
"json/json_reader_unittest.cc",
"json/json_value_converter_unittest.cc",
diff --git a/base/base.gyp b/base/base.gyp
index ec5444a..d3a55b8 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -519,6 +519,7 @@
'i18n/time_formatting_unittest.cc',
'i18n/timezone_unittest.cc',
'ios/device_util_unittest.mm',
+ 'ios/weak_nsobject_unittest.mm',
'json/json_parser_unittest.cc',
'json/json_reader_unittest.cc',
'json/json_value_converter_unittest.cc',
diff --git a/base/base.gypi b/base/base.gypi
index 60ed267..5ec4142 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -253,6 +253,8 @@
'ios/ios_util.mm',
'ios/scoped_critical_action.h',
'ios/scoped_critical_action.mm',
+ 'ios/weak_nsobject.h',
+ 'ios/weak_nsobject.mm',
'json/json_file_value_serializer.cc',
'json/json_file_value_serializer.h',
'json/json_parser.cc',
diff --git a/base/ios/weak_nsobject.h b/base/ios/weak_nsobject.h
new file mode 100644
index 0000000..46aecb5
--- /dev/null
+++ b/base/ios/weak_nsobject.h
@@ -0,0 +1,166 @@
+// 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.
+//
+// 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:
+ WeakContainer(id object) : object_(object) {}
+ id object() { 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 base::NonThreadSafe {
+ public:
+ explicit WeakNSProtocol(NST object = nil) {
+ container_ = [CRBWeakNSProtocolSentinel containerForObject:object];
+ }
+
+ WeakNSProtocol(const WeakNSProtocol<NST>& that) {
+ container_ = that.container_;
+ }
+
+ ~WeakNSProtocol() {
+ // A WeakNSProtocol object can be allocated on one thread and released on
+ // another. This is not the case for the contained object.
+ DetachFromThread();
+ }
+
+ void reset(NST object = nil) {
+ DCHECK(CalledOnValidThread());
+ container_ = [CRBWeakNSProtocolSentinel containerForObject:object];
+ }
+
+ NST get() const {
+ DCHECK(CalledOnValidThread());
+ if (!container_.get())
+ return nil;
+ return container_->object();
+ }
+
+ WeakNSProtocol& operator=(const WeakNSProtocol<NST>& that) {
+ DCHECK(CalledOnValidThread());
+ container_ = that.container_;
+ return *this;
+ }
+
+ bool operator==(NST that) const {
+ DCHECK(CalledOnValidThread());
+ return get() == that;
+ }
+
+ bool operator!=(NST that) const { return get() != that; }
+
+ operator NST() const { return get(); }
+
+ private:
+ // Refecounted reference to the container tracking the ObjectiveC object this
+ // class encapsulates.
+ scoped_refptr<base::WeakContainer> container_;
+};
+
+// 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_
diff --git a/base/ios/weak_nsobject.mm b/base/ios/weak_nsobject.mm
new file mode 100644
index 0000000..36f9d3e
--- /dev/null
+++ b/base/ios/weak_nsobject.mm
@@ -0,0 +1,61 @@
+// 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.
+
+#include "base/ios/weak_nsobject.h"
+
+#include "base/mac/scoped_nsautorelease_pool.h"
+#include "base/mac/scoped_nsobject.h"
+
+namespace {
+// The key needed by objc_setAssociatedObject.
+char sentinelObserverKey_;
+}
+
+@interface CRBWeakNSProtocolSentinel ()
+// Container to notify on dealloc.
+@property(readonly, assign) scoped_refptr<base::WeakContainer> container;
+// Designed initializer.
+- (id)initWithContainer:(scoped_refptr<base::WeakContainer>)container;
+@end
+
+@implementation CRBWeakNSProtocolSentinel
+
+@synthesize container = container_;
+
++ (scoped_refptr<base::WeakContainer>)containerForObject:(id)object {
+ if (object == nil)
+ return nullptr;
+ // The autoreleasePool is needed here as the call to objc_getAssociatedObject
+ // returns an autoreleased object which is better released sooner than later.
+ base::mac::ScopedNSAutoreleasePool pool;
+ CRBWeakNSProtocolSentinel* sentinel =
+ objc_getAssociatedObject(object, &sentinelObserverKey_);
+ if (!sentinel) {
+ base::scoped_nsobject<CRBWeakNSProtocolSentinel> newSentinel(
+ [[CRBWeakNSProtocolSentinel alloc]
+ initWithContainer:new base::WeakContainer(object)]);
+ sentinel = newSentinel;
+ objc_setAssociatedObject(object, &sentinelObserverKey_, sentinel,
+ OBJC_ASSOCIATION_RETAIN);
+ // The retain count is 2. One retain is due to the alloc, the other to the
+ // association with the weak object.
+ DCHECK_EQ(2u, [sentinel retainCount]);
+ }
+ return [sentinel container];
+}
+
+- (id)initWithContainer:(scoped_refptr<base::WeakContainer>)container {
+ DCHECK(container.get());
+ self = [super init];
+ if (self)
+ container_ = container;
+ return self;
+}
+
+- (void)dealloc {
+ self.container->nullify();
+ [super dealloc];
+}
+
+@end
diff --git a/base/ios/weak_nsobject_unittest.mm b/base/ios/weak_nsobject_unittest.mm
new file mode 100644
index 0000000..9758aed
--- /dev/null
+++ b/base/ios/weak_nsobject_unittest.mm
@@ -0,0 +1,97 @@
+// 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.
+
+#include "base/basictypes.h"
+#include "base/ios/weak_nsobject.h"
+#include "base/mac/scoped_nsobject.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::WeakNSObject;
+
+namespace {
+
+TEST(WeakNSObjectTest, WeakNSObject) {
+ base::scoped_nsobject<NSObject> p1([[NSObject alloc] init]);
+ WeakNSObject<NSObject> w1(p1);
+ EXPECT_TRUE(w1);
+ p1.reset();
+ EXPECT_FALSE(w1);
+}
+
+TEST(WeakNSObjectTest, MultipleWeakNSObject) {
+ base::scoped_nsobject<NSObject> p1([[NSObject alloc] init]);
+ WeakNSObject<NSObject> w1(p1);
+ WeakNSObject<NSObject> w2(w1);
+ EXPECT_TRUE(w1);
+ EXPECT_TRUE(w2);
+ EXPECT_TRUE(w1.get() == w2.get());
+ p1.reset();
+ EXPECT_FALSE(w1);
+ EXPECT_FALSE(w2);
+}
+
+TEST(WeakNSObjectTest, WeakNSObjectDies) {
+ base::scoped_nsobject<NSObject> p1([[NSObject alloc] init]);
+ {
+ WeakNSObject<NSObject> w1(p1);
+ EXPECT_TRUE(w1);
+ }
+}
+
+TEST(WeakNSObjectTest, WeakNSObjectReset) {
+ base::scoped_nsobject<NSObject> p1([[NSObject alloc] init]);
+ WeakNSObject<NSObject> w1(p1);
+ EXPECT_TRUE(w1);
+ w1.reset();
+ EXPECT_FALSE(w1);
+ EXPECT_TRUE(p1);
+ EXPECT_TRUE([p1 description]);
+}
+
+TEST(WeakNSObjectTest, WeakNSObjectResetWithObject) {
+ base::scoped_nsobject<NSObject> p1([[NSObject alloc] init]);
+ base::scoped_nsobject<NSObject> p2([[NSObject alloc] init]);
+ WeakNSObject<NSObject> w1(p1);
+ EXPECT_TRUE(w1);
+ w1.reset(p2);
+ EXPECT_TRUE(w1);
+ EXPECT_TRUE([p1 description]);
+ EXPECT_TRUE([p2 description]);
+}
+
+TEST(WeakNSObjectTest, WeakNSObjectEmpty) {
+ base::scoped_nsobject<NSObject> p1([[NSObject alloc] init]);
+ WeakNSObject<NSObject> w1;
+ EXPECT_FALSE(w1);
+ w1.reset(p1);
+ EXPECT_TRUE(w1);
+ p1.reset();
+ EXPECT_FALSE(w1);
+}
+
+TEST(WeakNSObjectTest, WeakNSObjectCopy) {
+ base::scoped_nsobject<NSObject> p1([[NSObject alloc] init]);
+ WeakNSObject<NSObject> w1(p1);
+ WeakNSObject<NSObject> w2(w1);
+ EXPECT_TRUE(w1);
+ EXPECT_TRUE(w2);
+ p1.reset();
+ EXPECT_FALSE(w1);
+ EXPECT_FALSE(w2);
+}
+
+TEST(WeakNSObjectTest, WeakNSObjectAssignment) {
+ base::scoped_nsobject<NSObject> p1([[NSObject alloc] init]);
+ WeakNSObject<NSObject> w1(p1);
+ WeakNSObject<NSObject> w2;
+ EXPECT_FALSE(w2);
+ w2 = w1;
+ EXPECT_TRUE(w1);
+ EXPECT_TRUE(w2);
+ p1.reset();
+ EXPECT_FALSE(w1);
+ EXPECT_FALSE(w2);
+}
+
+} // namespace