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