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#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
12
13#import <CoreFoundation/CoreFoundation.h>
14
15/**
16 * Wrapper class for managing lifetime of CoreFoundation objects. It will call
17 * CFRetain and CFRelease appropriately on creation, assignment, and deletion.
18 * Based on sk_sp<>.
19 */
20template <typename T> static inline T SkCFSafeRetain(T obj) {
21 if (obj) {
22 CFRetain(obj);
23 }
24 return obj;
25}
26
27template <typename T> static inline void SkCFSafeRelease(T obj) {
28 if (obj) {
29 CFRelease(obj);
30 }
31}
32
33template <typename T> class sk_cf_obj {
34public:
35 using element_type = T;
36
37 constexpr sk_cf_obj() : fObject(nullptr) {}
38
39 /**
40 * Shares the underlying object by calling CFRetain(), so that both the argument and the newly
41 * created sk_cf_obj both have a reference to it.
42 */
43 sk_cf_obj(const sk_cf_obj<T>& that) : fObject(SkCFSafeRetain(that.get())) {}
44
45 /**
46 * Move the underlying object from the argument to the newly created sk_cf_obj. Afterwards only
47 * the new sk_cf_obj will have a reference to the object, and the argument will point to null.
48 * No call to CFRetain() or CFRelease() will be made.
49 */
50 sk_cf_obj(sk_cf_obj<T>&& that) : fObject(that.release()) {}
51
52 /**
53 * Adopt the bare object into the newly created sk_cf_obj.
54 * No call to CFRetain() or CFRelease() will be made.
55 */
56 explicit sk_cf_obj(T obj) {
57 fObject = obj;
58 }
59
60 /**
61 * Calls CFRelease() on the underlying object pointer.
62 */
63 ~sk_cf_obj() {
64 SkCFSafeRelease(fObject);
65 SkDEBUGCODE(fObject = nullptr);
66 }
67
68 /**
69 * Shares the underlying object referenced by the argument by calling CFRetain() on it. If this
70 * sk_cf_obj previously had a reference to an object (i.e. not null) it will call CFRelease()
71 * on that object.
72 */
73 sk_cf_obj<T>& operator=(const sk_cf_obj<T>& that) {
74 if (this != &that) {
75 this->reset(SkCFSafeRetain(that.get()));
76 }
77 return *this;
78 }
79
80 /**
81 * Move the underlying object from the argument to the sk_cf_obj. If the sk_cf_obj
82 * previously held a reference to another object, CFRelease() will be called on that object.
83 * No call to CFRetain() will be made.
84 */
85 sk_cf_obj<T>& operator=(sk_cf_obj<T>&& that) {
86 this->reset(that.release());
87 return *this;
88 }
89
90 T get() const { return fObject; }
91
92 /**
93 * Adopt the new object, and call CFRelease() on any previously held object (if not null).
94 * No call to CFRetain() will be made.
95 */
96 void reset(T object = nullptr) {
97 T oldObject = fObject;
98 fObject = object;
99 SkCFSafeRelease(oldObject);
100 }
101
102 /**
103 * Shares the new object by calling CFRetain() on it. If this sk_cf_obj previously had a
104 * reference to an object (i.e. not null) it will call CFRelease() on that object.
105 */
106 void retain(T object) {
107 if (this->fObject != object) {
108 this->reset(SkCFSafeRetain(object));
109 }
110 }
111
112 /**
113 * Return the original object, and set the internal object to nullptr.
114 * The caller must assume ownership of the object, and manage its reference count directly.
115 * No call to CFRelease() will be made.
116 */
117 T SK_WARN_UNUSED_RESULT release() {
118 T obj = fObject;
119 fObject = nullptr;
120 return obj;
121 }
122
123private:
124 T fObject;
125};
126
127template <typename T> inline bool operator==(const sk_cf_obj<T>& a,
128 const sk_cf_obj<T>& b) {
129 return a.get() == b.get();
130}
131
132template <typename T> inline bool operator!=(const sk_cf_obj<T>& a,
133 const sk_cf_obj<T>& b) {
134 return a.get() != b.get();
135}
136
137#endif // SK_BUILD_FOR_MAC || SK_BUILD_FOR_IOS
138#endif // SkCFOBject_DEFINED
139