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