summaryrefslogtreecommitdiff
path: root/base/mac/scoped_nsobject.h
blob: ecd8e78f9d3fac3be11a2c543f4bbaf1ab2b55b6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
// Copyright (c) 2012 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_MAC_SCOPED_NSOBJECT_H_
#define BASE_MAC_SCOPED_NSOBJECT_H_

#include <type_traits>

// Include NSObject.h directly because Foundation.h pulls in many dependencies.
// (Approx 100k lines of code versus 1.5k for NSObject.h). scoped_nsobject gets
// singled out because it is most typically included from other header files.
#import <Foundation/NSObject.h>

#include "base/base_export.h"
#include "base/compiler_specific.h"
#include "base/mac/scoped_typeref.h"

#if !defined(__has_feature) || !__has_feature(objc_arc)
@class NSAutoreleasePool;
#endif

namespace base {

// scoped_nsobject<> is patterned after std::unique_ptr<>, but maintains
// ownership of an NSObject subclass object.  Style deviations here are solely
// for compatibility with std::unique_ptr<>'s interface, with which everyone is
// already familiar.
//
// scoped_nsobject<> takes ownership of an object (in the constructor or in
// reset()) by taking over the caller's existing ownership claim.  The caller
// must own the object it gives to scoped_nsobject<>, and relinquishes an
// ownership claim to that object.  scoped_nsobject<> does not call -retain,
// callers have to call this manually if appropriate.
//
// scoped_nsprotocol<> has the same behavior as scoped_nsobject, but can be used
// with protocols.
//
// scoped_nsobject<> is not to be used for NSAutoreleasePools. For
// NSAutoreleasePools use ScopedNSAutoreleasePool from
// scoped_nsautorelease_pool.h instead.
// We check for bad uses of scoped_nsobject and NSAutoreleasePool at compile
// time with a template specialization (see below).
//
// If Automatic Reference Counting (aka ARC) is enabled then the ownership
// policy is not controllable by the user as ARC make it really difficult to
// transfer ownership (the reference passed to scoped_nsobject constructor is
// sunk by ARC and __attribute((ns_consumed)) appears to not work correctly
// with Objective-C++ see https://llvm.org/bugs/show_bug.cgi?id=27887). Due to
// that, the policy is always to |RETAIN| when using ARC.

namespace internal {

BASE_EXPORT id ScopedNSProtocolTraitsRetain(__unsafe_unretained id obj)
    __attribute((ns_returns_not_retained));
BASE_EXPORT id ScopedNSProtocolTraitsAutoRelease(__unsafe_unretained id obj)
    __attribute((ns_returns_not_retained));
BASE_EXPORT void ScopedNSProtocolTraitsRelease(__unsafe_unretained id obj);

// Traits for ScopedTypeRef<>. As this class may be compiled from file with
// Automatic Reference Counting enable or not all methods have annotation to
// enforce the same code generation in both case (in particular, the Retain
// method uses ns_returns_not_retained to prevent ARC to insert a -release
// call on the returned value and thus defeating the -retain).
template <typename NST>
struct ScopedNSProtocolTraits {
  static NST InvalidValue() __attribute((ns_returns_not_retained)) {
    return nil;
  }
  static NST Retain(__unsafe_unretained NST nst)
      __attribute((ns_returns_not_retained)) {
    return ScopedNSProtocolTraitsRetain(nst);
  }
  static void Release(__unsafe_unretained NST nst) {
    ScopedNSProtocolTraitsRelease(nst);
  }
};

}  // namespace internal

template <typename NST>
class scoped_nsprotocol
    : public ScopedTypeRef<NST, internal::ScopedNSProtocolTraits<NST>> {
 public:
  using Traits = internal::ScopedNSProtocolTraits<NST>;

#if !defined(__has_feature) || !__has_feature(objc_arc)
  explicit scoped_nsprotocol(
      NST object = Traits::InvalidValue(),
      base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)
      : ScopedTypeRef<NST, Traits>(object, policy) {}
#else
  explicit scoped_nsprotocol(NST object = Traits::InvalidValue())
      : ScopedTypeRef<NST, Traits>(object, base::scoped_policy::RETAIN) {}
#endif

  scoped_nsprotocol(const scoped_nsprotocol<NST>& that)
      : ScopedTypeRef<NST, Traits>(that) {}

  template <typename NSR>
  explicit scoped_nsprotocol(const scoped_nsprotocol<NSR>& that_as_subclass)
      : ScopedTypeRef<NST, Traits>(that_as_subclass) {}

  scoped_nsprotocol(scoped_nsprotocol<NST>&& that)
      : ScopedTypeRef<NST, Traits>(std::move(that)) {}

  scoped_nsprotocol& operator=(const scoped_nsprotocol<NST>& that) {
    ScopedTypeRef<NST, Traits>::operator=(that);
    return *this;
  }

#if !defined(__has_feature) || !__has_feature(objc_arc)
  void reset(NST object = Traits::InvalidValue(),
             base::scoped_policy::OwnershipPolicy policy =
                 base::scoped_policy::ASSUME) {
    ScopedTypeRef<NST, Traits>::reset(object, policy);
  }
#else
  void reset(NST object = Traits::InvalidValue()) {
    ScopedTypeRef<NST, Traits>::reset(object, base::scoped_policy::RETAIN);
  }
#endif

  // Shift reference to the autorelease pool to be released later.
  NST autorelease() __attribute((ns_returns_not_retained)) {
    return internal::ScopedNSProtocolTraitsAutoRelease(this->release());
  }
};

// Free functions
template <class C>
void swap(scoped_nsprotocol<C>& p1, scoped_nsprotocol<C>& p2) {
  p1.swap(p2);
}

template <class C>
bool operator==(C p1, const scoped_nsprotocol<C>& p2) {
  return p1 == p2.get();
}

template <class C>
bool operator!=(C p1, const scoped_nsprotocol<C>& p2) {
  return p1 != p2.get();
}

template <typename NST>
class scoped_nsobject : public scoped_nsprotocol<NST*> {
 public:
  using Traits = typename scoped_nsprotocol<NST*>::Traits;

#if !defined(__has_feature) || !__has_feature(objc_arc)
  explicit scoped_nsobject(
      NST* object = Traits::InvalidValue(),
      base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)
      : scoped_nsprotocol<NST*>(object, policy) {}
#else
  explicit scoped_nsobject(NST* object = Traits::InvalidValue())
      : scoped_nsprotocol<NST*>(object) {}
#endif

  scoped_nsobject(const scoped_nsobject<NST>& that)
      : scoped_nsprotocol<NST*>(that) {}

  template <typename NSR>
  explicit scoped_nsobject(const scoped_nsobject<NSR>& that_as_subclass)
      : scoped_nsprotocol<NST*>(that_as_subclass) {}

  scoped_nsobject(scoped_nsobject<NST>&& that)
      : scoped_nsprotocol<NST*>(std::move(that)) {}

  scoped_nsobject& operator=(const scoped_nsobject<NST>& that) {
    scoped_nsprotocol<NST*>::operator=(that);
    return *this;
  }

#if !defined(__has_feature) || !__has_feature(objc_arc)
  void reset(NST* object = Traits::InvalidValue(),
             base::scoped_policy::OwnershipPolicy policy =
                 base::scoped_policy::ASSUME) {
    scoped_nsprotocol<NST*>::reset(object, policy);
  }
#else
  void reset(NST* object = Traits::InvalidValue()) {
    scoped_nsprotocol<NST*>::reset(object);
  }
#endif

#if !defined(__has_feature) || !__has_feature(objc_arc)
  static_assert(std::is_same<NST, NSAutoreleasePool>::value == false,
                "Use ScopedNSAutoreleasePool instead");
#endif
};

// Specialization to make scoped_nsobject<id> work.
template<>
class scoped_nsobject<id> : public scoped_nsprotocol<id> {
 public:
  using Traits = typename scoped_nsprotocol<id>::Traits;

#if !defined(__has_feature) || !__has_feature(objc_arc)
  explicit scoped_nsobject(
      id object = Traits::InvalidValue(),
      base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)
      : scoped_nsprotocol<id>(object, policy) {}
#else
  explicit scoped_nsobject(id object = Traits::InvalidValue())
      : scoped_nsprotocol<id>(object) {}
#endif

  scoped_nsobject(const scoped_nsobject<id>& that)
      : scoped_nsprotocol<id>(that) {}

  template <typename NSR>
  explicit scoped_nsobject(const scoped_nsobject<NSR>& that_as_subclass)
      : scoped_nsprotocol<id>(that_as_subclass) {}

  scoped_nsobject(scoped_nsobject<id>&& that)
      : scoped_nsprotocol<id>(std::move(that)) {}

  scoped_nsobject& operator=(const scoped_nsobject<id>& that) {
    scoped_nsprotocol<id>::operator=(that);
    return *this;
  }

#if !defined(__has_feature) || !__has_feature(objc_arc)
  void reset(id object = Traits::InvalidValue(),
             base::scoped_policy::OwnershipPolicy policy =
                 base::scoped_policy::ASSUME) {
    scoped_nsprotocol<id>::reset(object, policy);
  }
#else
  void reset(id object = Traits::InvalidValue()) {
    scoped_nsprotocol<id>::reset(object);
  }
#endif
};

}  // namespace base

#endif  // BASE_MAC_SCOPED_NSOBJECT_H_