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