// 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 // 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 #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 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 class scoped_nsprotocol : public ScopedTypeRef> { public: using Traits = internal::ScopedNSProtocolTraits; #if !defined(__has_feature) || !__has_feature(objc_arc) explicit constexpr scoped_nsprotocol( NST object = Traits::InvalidValue(), base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME) : ScopedTypeRef(object, policy) {} #else explicit constexpr scoped_nsprotocol(NST object = Traits::InvalidValue()) : ScopedTypeRef(object, base::scoped_policy::RETAIN) {} #endif scoped_nsprotocol(const scoped_nsprotocol& that) : ScopedTypeRef(that) {} template explicit scoped_nsprotocol(const scoped_nsprotocol& that_as_subclass) : ScopedTypeRef(that_as_subclass) {} scoped_nsprotocol(scoped_nsprotocol&& that) : ScopedTypeRef(std::move(that)) {} scoped_nsprotocol& operator=(const scoped_nsprotocol& that) { ScopedTypeRef::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::reset(object, policy); } #else void reset(NST object = Traits::InvalidValue()) { ScopedTypeRef::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 void swap(scoped_nsprotocol& p1, scoped_nsprotocol& p2) { p1.swap(p2); } template bool operator==(C p1, const scoped_nsprotocol& p2) { return p1 == p2.get(); } template bool operator!=(C p1, const scoped_nsprotocol& p2) { return p1 != p2.get(); } template class scoped_nsobject : public scoped_nsprotocol { public: using Traits = typename scoped_nsprotocol::Traits; #if !defined(__has_feature) || !__has_feature(objc_arc) explicit constexpr scoped_nsobject( NST* object = Traits::InvalidValue(), base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME) : scoped_nsprotocol(object, policy) {} #else explicit constexpr scoped_nsobject(NST* object = Traits::InvalidValue()) : scoped_nsprotocol(object) {} #endif scoped_nsobject(const scoped_nsobject& that) : scoped_nsprotocol(that) {} template explicit scoped_nsobject(const scoped_nsobject& that_as_subclass) : scoped_nsprotocol(that_as_subclass) {} scoped_nsobject(scoped_nsobject&& that) : scoped_nsprotocol(std::move(that)) {} scoped_nsobject& operator=(const scoped_nsobject& that) { scoped_nsprotocol::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::reset(object, policy); } #else void reset(NST* object = Traits::InvalidValue()) { scoped_nsprotocol::reset(object); } #endif #if !defined(__has_feature) || !__has_feature(objc_arc) static_assert(std::is_same::value == false, "Use ScopedNSAutoreleasePool instead"); #endif }; // Specialization to make scoped_nsobject work. template<> class scoped_nsobject : public scoped_nsprotocol { public: using Traits = typename scoped_nsprotocol::Traits; #if !defined(__has_feature) || !__has_feature(objc_arc) explicit constexpr scoped_nsobject( id object = Traits::InvalidValue(), base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME) : scoped_nsprotocol(object, policy) {} #else explicit constexpr scoped_nsobject(id object = Traits::InvalidValue()) : scoped_nsprotocol(object) {} #endif scoped_nsobject(const scoped_nsobject& that) : scoped_nsprotocol(that) {} template explicit scoped_nsobject(const scoped_nsobject& that_as_subclass) : scoped_nsprotocol(that_as_subclass) {} scoped_nsobject(scoped_nsobject&& that) : scoped_nsprotocol(std::move(that)) {} scoped_nsobject& operator=(const scoped_nsobject& that) { scoped_nsprotocol::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::reset(object, policy); } #else void reset(id object = Traits::InvalidValue()) { scoped_nsprotocol::reset(object); } #endif }; } // namespace base #endif // BASE_MAC_SCOPED_NSOBJECT_H_