// Copyright 2020 The Dawn Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef COMMON_NSREF_H_ #define COMMON_NSREF_H_ #include "common/RefBase.h" #import #if !defined(__OBJC__) # error "NSRef can only be used in Objective C/C++ code." #endif // This file contains smart pointers that automatically reference and release Objective C objects // and prototocals in a manner very similar to Ref<>. Note that NSRef<> and NSPRef's constructor add // a reference to the object by default, so the pattern to get a reference for a newly created // NSObject is the following: // // NSRef foo = AcquireNSRef([NSFoo alloc]); // // NSRef overloads -> and * but these operators don't work extremely well with Objective C's // features. For example automatic dereferencing when doing the following doesn't work: // // NSFoo* foo; // foo.member = 1; // someVar = foo.member; // // Instead use the message passing syntax: // // NSRef foo; // [*foo setMember: 1]; // someVar = [*foo member]; // // Also did you notive the extra '*' in the example above? That's because Objective C's message // passing doesn't automatically call a C++ operator to dereference smart pointers (like -> does) so // we have to dereference manually using '*'. In some cases the extra * or message passing syntax // can get a bit annoying so instead a local "naked" pointer can be borrowed from the NSRef. This // would change the syntax overload in the following: // // NSRef foo; // [*foo setA:1]; // [*foo setB:2]; // [*foo setC:3]; // // Into (note access to members of ObjC classes referenced via pointer is done with . and not ->): // // NSRef fooRef; // NSFoo* foo = fooRef.Get(); // foo.a = 1; // foo.b = 2; // boo.c = 3; // // Which can be subjectively easier to read. template struct NSRefTraits { static constexpr T kNullValue = nullptr; static void Reference(T value) { [value retain]; } static void Release(T value) { [value release]; } }; template class NSRef : public RefBase> { public: using RefBase>::RefBase; const T* operator*() const { return this->Get(); } T* operator*() { return this->Get(); } }; template NSRef AcquireNSRef(T* pointee) { NSRef ref; ref.Acquire(pointee); return ref; } // This is a RefBase<> for an Objective C protocol (hence the P). Objective C protocols must always // be referenced with id and not just ProtocolName* so they cannot use NSRef<> // itself. That's what the P in NSPRef stands for: Protocol. template class NSPRef : public RefBase> { public: using RefBase>::RefBase; const T operator*() const { return this->Get(); } T operator*() { return this->Get(); } }; template NSPRef AcquireNSPRef(T pointee) { NSPRef ref; ref.Acquire(pointee); return ref; } #endif // COMMON_NSREF_H_