1 // Copyright 2020 The Dawn Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef COMMON_NSREF_H_ 16 #define COMMON_NSREF_H_ 17 18 #include "common/RefBase.h" 19 20 #import <Foundation/NSObject.h> 21 22 #if !defined(__OBJC__) 23 # error "NSRef can only be used in Objective C/C++ code." 24 #endif 25 26 // This file contains smart pointers that automatically reference and release Objective C objects 27 // and prototocals in a manner very similar to Ref<>. Note that NSRef<> and NSPRef's constructor add 28 // a reference to the object by default, so the pattern to get a reference for a newly created 29 // NSObject is the following: 30 // 31 // NSRef<NSFoo> foo = AcquireNSRef([NSFoo alloc]); 32 // 33 // NSRef overloads -> and * but these operators don't work extremely well with Objective C's 34 // features. For example automatic dereferencing when doing the following doesn't work: 35 // 36 // NSFoo* foo; 37 // foo.member = 1; 38 // someVar = foo.member; 39 // 40 // Instead use the message passing syntax: 41 // 42 // NSRef<NSFoo> foo; 43 // [*foo setMember: 1]; 44 // someVar = [*foo member]; 45 // 46 // Also did you notive the extra '*' in the example above? That's because Objective C's message 47 // passing doesn't automatically call a C++ operator to dereference smart pointers (like -> does) so 48 // we have to dereference manually using '*'. In some cases the extra * or message passing syntax 49 // can get a bit annoying so instead a local "naked" pointer can be borrowed from the NSRef. This 50 // would change the syntax overload in the following: 51 // 52 // NSRef<NSFoo> foo; 53 // [*foo setA:1]; 54 // [*foo setB:2]; 55 // [*foo setC:3]; 56 // 57 // Into (note access to members of ObjC classes referenced via pointer is done with . and not ->): 58 // 59 // NSRef<NSFoo> fooRef; 60 // NSFoo* foo = fooRef.Get(); 61 // foo.a = 1; 62 // foo.b = 2; 63 // boo.c = 3; 64 // 65 // Which can be subjectively easier to read. 66 67 template <typename T> 68 struct NSRefTraits { 69 static constexpr T kNullValue = nullptr; ReferenceNSRefTraits70 static void Reference(T value) { 71 [value retain]; 72 } ReleaseNSRefTraits73 static void Release(T value) { 74 [value release]; 75 } 76 }; 77 78 template <typename T> 79 class NSRef : public RefBase<T*, NSRefTraits<T*>> { 80 public: 81 using RefBase<T*, NSRefTraits<T*>>::RefBase; 82 83 const T* operator*() const { 84 return this->Get(); 85 } 86 87 T* operator*() { 88 return this->Get(); 89 } 90 }; 91 92 template <typename T> AcquireNSRef(T * pointee)93NSRef<T> AcquireNSRef(T* pointee) { 94 NSRef<T> ref; 95 ref.Acquire(pointee); 96 return ref; 97 } 98 99 // This is a RefBase<> for an Objective C protocol (hence the P). Objective C protocols must always 100 // be referenced with id<ProtocolName> and not just ProtocolName* so they cannot use NSRef<> 101 // itself. That's what the P in NSPRef stands for: Protocol. 102 template <typename T> 103 class NSPRef : public RefBase<T, NSRefTraits<T>> { 104 public: 105 using RefBase<T, NSRefTraits<T>>::RefBase; 106 107 const T operator*() const { 108 return this->Get(); 109 } 110 111 T operator*() { 112 return this->Get(); 113 } 114 }; 115 116 template <typename T> AcquireNSPRef(T pointee)117NSPRef<T> AcquireNSPRef(T pointee) { 118 NSPRef<T> ref; 119 ref.Acquire(pointee); 120 return ref; 121 } 122 123 #endif // COMMON_NSREF_H_ 124