1 /*
2 * Copyright 2019 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #ifndef SkCFObject_DEFINED
9 #define SkCFObject_DEFINED
10
11 #ifdef __APPLE__
12
13 #include "include/core/SkTypes.h"
14
15 #include <cstddef> // std::nullptr_t
16
17 #import <CoreFoundation/CoreFoundation.h>
18
19 /**
20 * Wrapper class for managing lifetime of CoreFoundation objects. It will call
21 * CFRetain and CFRelease appropriately on creation, assignment, and deletion.
22 * Based on sk_sp<>.
23 */
SkCFSafeRetain(T obj)24 template <typename T> static inline T SkCFSafeRetain(T obj) {
25 if (obj) {
26 CFRetain(obj);
27 }
28 return obj;
29 }
30
SkCFSafeRelease(T obj)31 template <typename T> static inline void SkCFSafeRelease(T obj) {
32 if (obj) {
33 CFRelease(obj);
34 }
35 }
36
37 template <typename T> class sk_cfp {
38 public:
39 using element_type = T;
40
sk_cfp()41 constexpr sk_cfp() {}
sk_cfp(std::nullptr_t)42 constexpr sk_cfp(std::nullptr_t) {}
43
44 /**
45 * Shares the underlying object by calling CFRetain(), so that both the argument and the newly
46 * created sk_cfp both have a reference to it.
47 */
sk_cfp(const sk_cfp<T> & that)48 sk_cfp(const sk_cfp<T>& that) : fObject(SkCFSafeRetain(that.get())) {}
49
50 /**
51 * Move the underlying object from the argument to the newly created sk_cfp. Afterwards only
52 * the new sk_cfp will have a reference to the object, and the argument will point to null.
53 * No call to CFRetain() or CFRelease() will be made.
54 */
sk_cfp(sk_cfp<T> && that)55 sk_cfp(sk_cfp<T>&& that) : fObject(that.release()) {}
56
57 /**
58 * Adopt the bare object into the newly created sk_cfp.
59 * No call to CFRetain() or CFRelease() will be made.
60 */
sk_cfp(T obj)61 explicit sk_cfp(T obj) {
62 fObject = obj;
63 }
64
65 /**
66 * Calls CFRelease() on the underlying object pointer.
67 */
~sk_cfp()68 ~sk_cfp() {
69 SkCFSafeRelease(fObject);
70 SkDEBUGCODE(fObject = nil);
71 }
72
73 sk_cfp<T>& operator=(std::nullptr_t) { this->reset(); return *this; }
74
75 /**
76 * Shares the underlying object referenced by the argument by calling CFRetain() on it. If this
77 * sk_cfp previously had a reference to an object (i.e. not null) it will call CFRelease()
78 * on that object.
79 */
80 sk_cfp<T>& operator=(const sk_cfp<T>& that) {
81 if (this != &that) {
82 this->reset(SkCFSafeRetain(that.get()));
83 }
84 return *this;
85 }
86
87 /**
88 * Move the underlying object from the argument to the sk_cfp. If the sk_cfp
89 * previously held a reference to another object, CFRelease() will be called on that object.
90 * No call to CFRetain() will be made.
91 */
92 sk_cfp<T>& operator=(sk_cfp<T>&& that) {
93 this->reset(that.release());
94 return *this;
95 }
96
97 explicit operator bool() const { return this->get() != nil; }
98
get()99 T get() const { return fObject; }
100 T operator*() const {
101 SkASSERT(fObject);
102 return fObject;
103 }
104
105 /**
106 * Adopt the new object, and call CFRelease() on any previously held object (if not null).
107 * No call to CFRetain() will be made.
108 */
109 void reset(T object = nil) {
110 // Need to unref after assigning, see
111 // http://wg21.cmeerw.net/lwg/issue998
112 // http://wg21.cmeerw.net/lwg/issue2262
113 T oldObject = fObject;
114 fObject = object;
115 SkCFSafeRelease(oldObject);
116 }
117
118 /**
119 * Shares the new object by calling CFRetain() on it. If this sk_cfp previously had a
120 * reference to an object (i.e. not null) it will call CFRelease() on that object.
121 */
retain(T object)122 void retain(T object) {
123 if (fObject != object) {
124 this->reset(SkCFSafeRetain(object));
125 }
126 }
127
128 /**
129 * Return the original object, and set the internal object to nullptr.
130 * The caller must assume ownership of the object, and manage its reference count directly.
131 * No call to CFRelease() will be made.
132 */
release()133 T SK_WARN_UNUSED_RESULT release() {
134 T obj = fObject;
135 fObject = nil;
136 return obj;
137 }
138
139 private:
140 T fObject = nil;
141 };
142
143 template <typename T> inline bool operator==(const sk_cfp<T>& a,
144 const sk_cfp<T>& b) {
145 return a.get() == b.get();
146 }
147 template <typename T> inline bool operator==(const sk_cfp<T>& a,
148 std::nullptr_t) {
149 return !a;
150 }
151 template <typename T> inline bool operator==(std::nullptr_t,
152 const sk_cfp<T>& b) {
153 return !b;
154 }
155
156 template <typename T> inline bool operator!=(const sk_cfp<T>& a,
157 const sk_cfp<T>& b) {
158 return a.get() != b.get();
159 }
160 template <typename T> inline bool operator!=(const sk_cfp<T>& a,
161 std::nullptr_t) {
162 return static_cast<bool>(a);
163 }
164 template <typename T> inline bool operator!=(std::nullptr_t,
165 const sk_cfp<T>& b) {
166 return static_cast<bool>(b);
167 }
168
169 /*
170 * Returns a sk_cfp wrapping the provided object AND calls retain on it (if not null).
171 *
172 * This is different than the semantics of the constructor for sk_cfp, which just wraps the
173 * object, effectively "adopting" it.
174 */
sk_ret_cfp(T obj)175 template <typename T> sk_cfp<T> sk_ret_cfp(T obj) {
176 return sk_cfp<T>(SkCFSafeRetain(obj));
177 }
178
179 // For Flutter.
180 // TODO: migrate them away from this and remove
181 template <typename T> using sk_cf_obj = sk_cfp<T>;
182
183 #endif // __APPLE__
184 #endif // SkCFObject_DEFINED
185