• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
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 BASE_MAC_SCOPED_NSOBJECT_H_
6 #define BASE_MAC_SCOPED_NSOBJECT_H_
7 
8 // Include NSObject.h directly because Foundation.h pulls in many dependencies.
9 // (Approx 100k lines of code versus 1.5k for NSObject.h). scoped_nsobject gets
10 // singled out because it is most typically included from other header files.
11 #import <Foundation/NSObject.h>
12 
13 #include <type_traits>
14 #include <utility>
15 
16 #include "base/base_export.h"
17 #include "base/compiler_specific.h"
18 #include "base/mac/scoped_typeref.h"
19 
20 #if !defined(__has_feature) || !__has_feature(objc_arc)
21 @class NSAutoreleasePool;
22 #endif
23 
24 // Note that this uses the direct runtime interface to reference counting.
25 // https://clang.llvm.org/docs/AutomaticReferenceCounting.html#runtime-support
26 // This is so this can work when compiled for ARC. Annotations are used to lie
27 // about the effects of these calls so that ARC will not insert any reference
28 // count calls of its own and leave the maintenance of the reference count to
29 // this class.
30 
31 extern "C" {
32 id objc_retain(__unsafe_unretained id value)
33     __attribute__((ns_returns_not_retained));
34 void objc_release(__unsafe_unretained id value);
35 id objc_autorelease(__unsafe_unretained id value)
36     __attribute__((ns_returns_not_retained));
37 }
38 
39 namespace base {
40 
41 // scoped_nsobject<> is patterned after std::unique_ptr<>, but maintains
42 // ownership of an NSObject subclass object.  Style deviations here are solely
43 // for compatibility with std::unique_ptr<>'s interface, with which everyone is
44 // already familiar.
45 //
46 // scoped_nsobject<> takes ownership of an object (in the constructor or in
47 // reset()) by taking over the caller's existing ownership claim.  The caller
48 // must own the object it gives to scoped_nsobject<>, and relinquishes an
49 // ownership claim to that object.  scoped_nsobject<> does not call -retain,
50 // callers have to call this manually if appropriate.
51 //
52 // scoped_nsprotocol<> has the same behavior as scoped_nsobject, but can be used
53 // with protocols.
54 //
55 // scoped_nsobject<> is not to be used for NSAutoreleasePools. For C++ code use
56 // NSAutoreleasePool; for Objective-C(++) code use @autoreleasepool instead. We
57 // check for bad uses of scoped_nsobject and NSAutoreleasePool at compile time
58 // with a template specialization (see below).
59 //
60 // If Automatic Reference Counting (aka ARC) is enabled then the ownership
61 // policy is not controllable by the user as ARC make it really difficult to
62 // transfer ownership (the reference passed to scoped_nsobject constructor is
63 // sunk by ARC, and a design that a function will maybe consume an argument
64 // based on a different argument isn't expressible with attributes). Due to
65 // that, the policy is always to |RETAIN| when using ARC.
66 
67 namespace internal {
68 
69 template <typename NST>
70 struct ScopedNSProtocolTraits {
InvalidValueScopedNSProtocolTraits71   static NST InvalidValue() { return nil; }
RetainScopedNSProtocolTraits72   static NST Retain(__unsafe_unretained NST nst) { return objc_retain(nst); }
ReleaseScopedNSProtocolTraits73   static void Release(__unsafe_unretained NST nst) { objc_release(nst); }
74 };
75 
76 }  // namespace internal
77 
78 template <typename NST>
79 class scoped_nsprotocol
80     : public ScopedTypeRef<NST, internal::ScopedNSProtocolTraits<NST>> {
81  public:
82   using Traits = internal::ScopedNSProtocolTraits<NST>;
83 
84 #if !defined(__has_feature) || !__has_feature(objc_arc)
85   explicit constexpr scoped_nsprotocol(
86       NST object = Traits::InvalidValue(),
87       base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)
88       : ScopedTypeRef<NST, Traits>(object, policy) {}
89 #else
90   explicit constexpr scoped_nsprotocol(NST object = Traits::InvalidValue())
91       : ScopedTypeRef<NST, Traits>(object, base::scoped_policy::RETAIN) {}
92 #endif
93 
scoped_nsprotocol(const scoped_nsprotocol<NST> & that)94   scoped_nsprotocol(const scoped_nsprotocol<NST>& that)
95       : ScopedTypeRef<NST, Traits>(that) {}
96 
97   template <typename NSR>
scoped_nsprotocol(const scoped_nsprotocol<NSR> & that_as_subclass)98   explicit scoped_nsprotocol(const scoped_nsprotocol<NSR>& that_as_subclass)
99       : ScopedTypeRef<NST, Traits>(that_as_subclass) {}
100 
scoped_nsprotocol(scoped_nsprotocol<NST> && that)101   scoped_nsprotocol(scoped_nsprotocol<NST>&& that)
102       : ScopedTypeRef<NST, Traits>(std::move(that)) {}
103 
104   scoped_nsprotocol& operator=(const scoped_nsprotocol<NST>& that) {
105     ScopedTypeRef<NST, Traits>::operator=(that);
106     return *this;
107   }
108 
reset(const scoped_nsprotocol<NST> & that)109   void reset(const scoped_nsprotocol<NST>& that) {
110     ScopedTypeRef<NST, Traits>::reset(that);
111   }
112 
113 #if !defined(__has_feature) || !__has_feature(objc_arc)
114   void reset(NST object = Traits::InvalidValue(),
115              base::scoped_policy::OwnershipPolicy policy =
116                  base::scoped_policy::ASSUME) {
117     ScopedTypeRef<NST, Traits>::reset(object, policy);
118   }
119 #else
120   void reset(NST object = Traits::InvalidValue()) {
121     ScopedTypeRef<NST, Traits>::reset(object, base::scoped_policy::RETAIN);
122   }
123 #endif
124 
125   // Shift reference to the autorelease pool to be released later.
autorelease()126   NST autorelease() __attribute__((ns_returns_not_retained)) {
127     return objc_autorelease(this->release());
128   }
129 };
130 
131 // Free functions
132 template <class C>
swap(scoped_nsprotocol<C> & p1,scoped_nsprotocol<C> & p2)133 void swap(scoped_nsprotocol<C>& p1, scoped_nsprotocol<C>& p2) {
134   p1.swap(p2);
135 }
136 
137 template <class C>
138 bool operator==(C p1, const scoped_nsprotocol<C>& p2) {
139   return p1 == p2.get();
140 }
141 
142 template <class C>
143 bool operator!=(C p1, const scoped_nsprotocol<C>& p2) {
144   return p1 != p2.get();
145 }
146 
147 template <typename NST>
148 class scoped_nsobject : public scoped_nsprotocol<NST*> {
149  public:
150   using Traits = typename scoped_nsprotocol<NST*>::Traits;
151 
152 #if !defined(__has_feature) || !__has_feature(objc_arc)
153   explicit constexpr scoped_nsobject(
154       NST* object = Traits::InvalidValue(),
155       base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)
156       : scoped_nsprotocol<NST*>(object, policy) {}
157 #else
158   explicit constexpr scoped_nsobject(NST* object = Traits::InvalidValue())
159       : scoped_nsprotocol<NST*>(object) {}
160 #endif
161 
scoped_nsobject(const scoped_nsobject<NST> & that)162   scoped_nsobject(const scoped_nsobject<NST>& that)
163       : scoped_nsprotocol<NST*>(that) {}
164 
165   template <typename NSR>
scoped_nsobject(const scoped_nsobject<NSR> & that_as_subclass)166   explicit scoped_nsobject(const scoped_nsobject<NSR>& that_as_subclass)
167       : scoped_nsprotocol<NST*>(that_as_subclass) {}
168 
scoped_nsobject(scoped_nsobject<NST> && that)169   scoped_nsobject(scoped_nsobject<NST>&& that)
170       : scoped_nsprotocol<NST*>(std::move(that)) {}
171 
172   scoped_nsobject& operator=(const scoped_nsobject<NST>& that) {
173     scoped_nsprotocol<NST*>::operator=(that);
174     return *this;
175   }
176 
reset(const scoped_nsobject<NST> & that)177   void reset(const scoped_nsobject<NST>& that) {
178     scoped_nsprotocol<NST*>::reset(that);
179   }
180 
181 #if !defined(__has_feature) || !__has_feature(objc_arc)
182   void reset(NST* object = Traits::InvalidValue(),
183              base::scoped_policy::OwnershipPolicy policy =
184                  base::scoped_policy::ASSUME) {
185     scoped_nsprotocol<NST*>::reset(object, policy);
186   }
187 #else
188   void reset(NST* object = Traits::InvalidValue()) {
189     scoped_nsprotocol<NST*>::reset(object);
190   }
191 #endif
192 
193 #if !defined(__has_feature) || !__has_feature(objc_arc)
194   static_assert(std::is_same<NST, NSAutoreleasePool>::value == false,
195                 "Use @autoreleasepool instead");
196 #endif
197 };
198 
199 // Specialization to make scoped_nsobject<id> work.
200 template<>
201 class scoped_nsobject<id> : public scoped_nsprotocol<id> {
202  public:
203   using Traits = typename scoped_nsprotocol<id>::Traits;
204 
205 #if !defined(__has_feature) || !__has_feature(objc_arc)
206   explicit constexpr scoped_nsobject(
207       id object = Traits::InvalidValue(),
208       base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)
209       : scoped_nsprotocol<id>(object, policy) {}
210 #else
211   explicit constexpr scoped_nsobject(id object = Traits::InvalidValue())
212       : scoped_nsprotocol<id>(object) {}
213 #endif
214 
215   scoped_nsobject(const scoped_nsobject<id>& that) = default;
216 
217   template <typename NSR>
scoped_nsobject(const scoped_nsobject<NSR> & that_as_subclass)218   explicit scoped_nsobject(const scoped_nsobject<NSR>& that_as_subclass)
219       : scoped_nsprotocol<id>(that_as_subclass) {}
220 
scoped_nsobject(scoped_nsobject<id> && that)221   scoped_nsobject(scoped_nsobject<id>&& that)
222       : scoped_nsprotocol<id>(std::move(that)) {}
223 
224   scoped_nsobject& operator=(const scoped_nsobject<id>& that) = default;
225 
reset(const scoped_nsobject<id> & that)226   void reset(const scoped_nsobject<id>& that) {
227     scoped_nsprotocol<id>::reset(that);
228   }
229 
230 #if !defined(__has_feature) || !__has_feature(objc_arc)
231   void reset(id object = Traits::InvalidValue(),
232              base::scoped_policy::OwnershipPolicy policy =
233                  base::scoped_policy::ASSUME) {
234     scoped_nsprotocol<id>::reset(object, policy);
235   }
236 #else
237   void reset(id object = Traits::InvalidValue()) {
238     scoped_nsprotocol<id>::reset(object);
239   }
240 #endif
241 };
242 
243 }  // namespace base
244 
245 #endif  // BASE_MAC_SCOPED_NSOBJECT_H_
246