Commit d21c92ce authored by Sylvain Defresne's avatar Sylvain Defresne Committed by Commit Bot

[ios/mac] Remove support for scoped_{nsobject,block} with ARC

As iOS has fully migrated to ARC (automatic reference counting)
and there is no traction to migration macOS to ARC, remove the
support allowing to use scoped_{nsobject,block} in files build
with ARC.

The guards are not necessary as scoped_{nsobject,block} won't
build with ARC since they contains calls to -release and cast
blocks to void*. However, they make it easier to understand
that this is not supported, so add them for documentation.

Bug: 1051997
Change-Id: I3db04ec645cb1830b92003723631c119e6e4a94c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2082385
Commit-Queue: Sylvain Defresne <sdefresne@chromium.org>
Reviewed-by: default avatarMark Mentovai <mark@chromium.org>
Cr-Commit-Position: refs/heads/master@{#746425}
parent 7fc01de2
......@@ -1139,7 +1139,6 @@ jumbo_component("base") {
"mac/scoped_nsautorelease_pool.h",
"mac/scoped_nsautorelease_pool.mm",
"mac/scoped_nsobject.h",
"mac/scoped_nsobject.mm",
"mac/scoped_objc_class_swizzler.h",
"mac/scoped_objc_class_swizzler.mm",
"mac/scoped_sending_event.h",
......@@ -1993,7 +1992,6 @@ jumbo_component("base") {
"mac/scoped_nsautorelease_pool.h",
"mac/scoped_nsautorelease_pool.mm",
"mac/scoped_nsobject.h",
"mac/scoped_nsobject.mm",
"mac/scoped_objc_class_swizzler.h",
"mac/scoped_objc_class_swizzler.mm",
"mac/scoped_typeref.h",
......@@ -2435,10 +2433,7 @@ bundle_data("base_unittests_bundle_data") {
if (is_ios || is_mac) {
source_set("base_unittests_arc") {
testonly = true
sources = [
"mac/bind_objc_block_unittest_arc.mm",
"mac/scoped_nsobject_unittest_arc.mm",
]
sources = [ "mac/bind_objc_block_unittest_arc.mm" ]
configs += [ "//build/config/compiler:enable_arc" ]
deps = [
":base",
......
......@@ -10,9 +10,7 @@
#include "base/mac/scoped_typeref.h"
#if defined(__has_feature) && __has_feature(objc_arc)
#define BASE_MAC_BRIDGE_CAST(TYPE, VALUE) (__bridge TYPE)(VALUE)
#else
#define BASE_MAC_BRIDGE_CAST(TYPE, VALUE) VALUE
#error "Cannot include base/mac/scoped_block.h in file built with ARC."
#endif
namespace base {
......@@ -23,13 +21,8 @@ namespace internal {
template <typename B>
struct ScopedBlockTraits {
static B InvalidValue() { return nullptr; }
static B Retain(B block) {
return BASE_MAC_BRIDGE_CAST(
B, Block_copy(BASE_MAC_BRIDGE_CAST(const void*, block)));
}
static void Release(B block) {
Block_release(BASE_MAC_BRIDGE_CAST(const void*, block));
}
static B Retain(B block) { return Block_copy(block); }
static void Release(B block) { Block_release(block); }
};
} // namespace internal
......@@ -37,36 +30,9 @@ struct ScopedBlockTraits {
// ScopedBlock<> is patterned after ScopedCFTypeRef<>, but uses Block_copy() and
// Block_release() instead of CFRetain() and CFRelease().
template <typename B>
class ScopedBlock : public ScopedTypeRef<B, internal::ScopedBlockTraits<B>> {
public:
using Traits = internal::ScopedBlockTraits<B>;
#if !defined(__has_feature) || !__has_feature(objc_arc)
explicit ScopedBlock(
B block = Traits::InvalidValue(),
base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)
: ScopedTypeRef<B, Traits>(block, policy) {}
#else
explicit ScopedBlock(B block = Traits::InvalidValue())
: ScopedTypeRef<B, Traits>(block, base::scoped_policy::RETAIN) {}
#endif
#if !defined(__has_feature) || !__has_feature(objc_arc)
void reset(B block = Traits::InvalidValue(),
base::scoped_policy::OwnershipPolicy policy =
base::scoped_policy::ASSUME) {
ScopedTypeRef<B, Traits>::reset(block, policy);
}
#else
void reset(B block = Traits::InvalidValue()) {
ScopedTypeRef<B, Traits>::reset(block, base::scoped_policy::RETAIN);
}
#endif
};
using ScopedBlock = ScopedTypeRef<B, internal::ScopedBlockTraits<B>>;
} // namespace mac
} // namespace base
#undef BASE_MAC_BRIDGE_CAST
#endif // BASE_MAC_SCOPED_BLOCK_H_
......@@ -16,10 +16,12 @@
#include "base/compiler_specific.h"
#include "base/mac/scoped_typeref.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
@class NSAutoreleasePool;
#if defined(__has_feature) && __has_feature(objc_arc)
#error "Cannot include base/mac/scoped_nsobject.h in file built with ARC."
#endif
@class NSAutoreleasePool;
namespace base {
// scoped_nsobject<> is patterned after std::unique_ptr<>, but maintains
......@@ -40,39 +42,14 @@ namespace base {
// NSAutoreleasePool; for Objective-C(++) code use @autoreleasepool 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 <typename NST>
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);
}
static NST InvalidValue() { return nil; }
static NST Retain(NST nst) { return [nst retain]; }
static void Release(NST nst) { [nst release]; }
};
} // namespace internal
......@@ -81,49 +58,11 @@ template <typename NST>
class scoped_nsprotocol
: public ScopedTypeRef<NST, internal::ScopedNSProtocolTraits<NST>> {
public:
using Traits = internal::ScopedNSProtocolTraits<NST>;
#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<NST, Traits>(object, policy) {}
#else
explicit constexpr scoped_nsprotocol(NST object = Traits::InvalidValue())
: ScopedTypeRef<NST, Traits>(object, base::scoped_policy::RETAIN) {}
#endif
scoped_nsprotocol(const scoped_nsprotocol<NST>& that)
: ScopedTypeRef<NST, Traits>(that) {}
template <typename NSR>
explicit scoped_nsprotocol(const scoped_nsprotocol<NSR>& that_as_subclass)
: ScopedTypeRef<NST, Traits>(that_as_subclass) {}
scoped_nsprotocol(scoped_nsprotocol<NST>&& that)
: ScopedTypeRef<NST, Traits>(std::move(that)) {}
scoped_nsprotocol& operator=(const scoped_nsprotocol<NST>& that) {
ScopedTypeRef<NST, Traits>::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<NST, Traits>::reset(object, policy);
}
#else
void reset(NST object = Traits::InvalidValue()) {
ScopedTypeRef<NST, Traits>::reset(object, base::scoped_policy::RETAIN);
}
#endif
using ScopedTypeRef<NST,
internal::ScopedNSProtocolTraits<NST>>::ScopedTypeRef;
// Shift reference to the autorelease pool to be released later.
NST autorelease() __attribute((ns_returns_not_retained)) {
return internal::ScopedNSProtocolTraitsAutoRelease(this->release());
}
NST autorelease() { return [this->release() autorelease]; }
};
// Free functions
......@@ -145,93 +84,17 @@ bool operator!=(C p1, const scoped_nsprotocol<C>& p2) {
template <typename NST>
class scoped_nsobject : public scoped_nsprotocol<NST*> {
public:
using Traits = typename scoped_nsprotocol<NST*>::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<NST*>(object, policy) {}
#else
explicit constexpr scoped_nsobject(NST* object = Traits::InvalidValue())
: scoped_nsprotocol<NST*>(object) {}
#endif
scoped_nsobject(const scoped_nsobject<NST>& that)
: scoped_nsprotocol<NST*>(that) {}
template <typename NSR>
explicit scoped_nsobject(const scoped_nsobject<NSR>& that_as_subclass)
: scoped_nsprotocol<NST*>(that_as_subclass) {}
scoped_nsobject(scoped_nsobject<NST>&& that)
: scoped_nsprotocol<NST*>(std::move(that)) {}
scoped_nsobject& operator=(const scoped_nsobject<NST>& that) {
scoped_nsprotocol<NST*>::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<NST*>::reset(object, policy);
}
#else
void reset(NST* object = Traits::InvalidValue()) {
scoped_nsprotocol<NST*>::reset(object);
}
#endif
using scoped_nsprotocol<NST*>::scoped_nsprotocol;
#if !defined(__has_feature) || !__has_feature(objc_arc)
static_assert(std::is_same<NST, NSAutoreleasePool>::value == false,
"Use @autoreleasepool instead");
#endif
};
// Specialization to make scoped_nsobject<id> work.
template<>
class scoped_nsobject<id> : public scoped_nsprotocol<id> {
public:
using Traits = typename scoped_nsprotocol<id>::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<id>(object, policy) {}
#else
explicit constexpr scoped_nsobject(id object = Traits::InvalidValue())
: scoped_nsprotocol<id>(object) {}
#endif
scoped_nsobject(const scoped_nsobject<id>& that)
: scoped_nsprotocol<id>(that) {}
template <typename NSR>
explicit scoped_nsobject(const scoped_nsobject<NSR>& that_as_subclass)
: scoped_nsprotocol<id>(that_as_subclass) {}
scoped_nsobject(scoped_nsobject<id>&& that)
: scoped_nsprotocol<id>(std::move(that)) {}
scoped_nsobject& operator=(const scoped_nsobject<id>& that) {
scoped_nsprotocol<id>::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<id>::reset(object, policy);
}
#else
void reset(id object = Traits::InvalidValue()) {
scoped_nsprotocol<id>::reset(object);
}
#endif
using scoped_nsprotocol<id>::scoped_nsprotocol;
};
} // namespace base
......
// Copyright 2016 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.
#import "base/mac/scoped_nsobject.h"
namespace base {
namespace internal {
id ScopedNSProtocolTraitsRetain(id obj) {
return [obj retain];
}
id ScopedNSProtocolTraitsAutoRelease(id obj) {
return [obj autorelease];
}
void ScopedNSProtocolTraitsRelease(id obj) {
return [obj release];
}
} // namespace internal
} // namespace base
// 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.
#include <vector>
#import <CoreFoundation/CoreFoundation.h>
#include "base/logging.h"
#import "base/mac/scoped_nsobject.h"
#include "testing/gtest/include/gtest/gtest.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
template <typename NST>
CFIndex GetRetainCount(const base::scoped_nsobject<NST>& nst) {
@autoreleasepool {
return CFGetRetainCount((__bridge CFTypeRef)nst.get()) - 1;
}
}
#if __has_feature(objc_arc_weak)
TEST(ScopedNSObjectTestARC, DefaultPolicyIsRetain) {
__weak id o;
@autoreleasepool {
base::scoped_nsprotocol<id> p([[NSObject alloc] init]);
o = p.get();
DCHECK_EQ(o, p.get());
}
DCHECK_EQ(o, nil);
}
#endif
TEST(ScopedNSObjectTestARC, ScopedNSObject) {
base::scoped_nsobject<NSObject> p1([[NSObject alloc] init]);
@autoreleasepool {
EXPECT_TRUE(p1.get());
EXPECT_TRUE(p1.get());
}
EXPECT_EQ(1, GetRetainCount(p1));
EXPECT_EQ(1, GetRetainCount(p1));
base::scoped_nsobject<NSObject> p2(p1);
@autoreleasepool {
EXPECT_EQ(p1.get(), p2.get());
}
EXPECT_EQ(2, GetRetainCount(p1));
p2.reset();
EXPECT_EQ(nil, p2.get());
EXPECT_EQ(1, GetRetainCount(p1));
{
base::scoped_nsobject<NSObject> p3 = p1;
@autoreleasepool {
EXPECT_EQ(p1.get(), p3.get());
}
EXPECT_EQ(2, GetRetainCount(p1));
@autoreleasepool {
p3 = p1;
EXPECT_EQ(p1.get(), p3.get());
}
EXPECT_EQ(2, GetRetainCount(p1));
}
EXPECT_EQ(1, GetRetainCount(p1));
base::scoped_nsobject<NSObject> p4;
@autoreleasepool {
p4 = base::scoped_nsobject<NSObject>(p1.get());
}
EXPECT_EQ(2, GetRetainCount(p1));
@autoreleasepool {
EXPECT_TRUE(p1 == p1.get());
EXPECT_TRUE(p1 == p1);
EXPECT_FALSE(p1 != p1);
EXPECT_FALSE(p1 != p1.get());
}
base::scoped_nsobject<NSObject> p5([[NSObject alloc] init]);
@autoreleasepool {
EXPECT_TRUE(p1 != p5);
EXPECT_TRUE(p1 != p5.get());
EXPECT_FALSE(p1 == p5);
EXPECT_FALSE(p1 == p5.get());
}
base::scoped_nsobject<NSObject> p6 = p1;
EXPECT_EQ(3, GetRetainCount(p6));
@autoreleasepool {
p6.autorelease();
EXPECT_EQ(nil, p6.get());
}
EXPECT_EQ(2, GetRetainCount(p1));
}
TEST(ScopedNSObjectTestARC, ScopedNSObjectInContainer) {
base::scoped_nsobject<id> p([[NSObject alloc] init]);
@autoreleasepool {
EXPECT_TRUE(p.get());
}
EXPECT_EQ(1, GetRetainCount(p));
@autoreleasepool {
std::vector<base::scoped_nsobject<id>> objects;
objects.push_back(p);
EXPECT_EQ(2, GetRetainCount(p));
@autoreleasepool {
EXPECT_EQ(p.get(), objects[0].get());
}
objects.push_back(base::scoped_nsobject<id>([[NSObject alloc] init]));
@autoreleasepool {
EXPECT_TRUE(objects[1].get());
}
EXPECT_EQ(1, GetRetainCount(objects[1]));
}
EXPECT_EQ(1, GetRetainCount(p));
}
TEST(ScopedNSObjectTestARC, ScopedNSObjectFreeFunctions) {
base::scoped_nsobject<id> p1([[NSObject alloc] init]);
id o1 = p1.get();
EXPECT_TRUE(o1 == p1);
EXPECT_FALSE(o1 != p1);
base::scoped_nsobject<id> p2([[NSObject alloc] init]);
EXPECT_TRUE(o1 != p2);
EXPECT_FALSE(o1 == p2);
id o2 = p2.get();
swap(p1, p2);
EXPECT_EQ(o2, p1.get());
EXPECT_EQ(o1, p2.get());
}
} // namespace
......@@ -54,7 +54,7 @@ class ScopedTypeRef {
typedef T element_type;
explicit constexpr ScopedTypeRef(
__unsafe_unretained T object = Traits::InvalidValue(),
T object = Traits::InvalidValue(),
base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)
: object_(object) {
if (object_ && policy == base::scoped_policy::RETAIN)
......@@ -97,7 +97,7 @@ class ScopedTypeRef {
return &object_;
}
void reset(__unsafe_unretained T object = Traits::InvalidValue(),
void reset(T object = Traits::InvalidValue(),
base::scoped_policy::OwnershipPolicy policy =
base::scoped_policy::ASSUME) {
if (object && policy == base::scoped_policy::RETAIN)
......@@ -107,16 +107,16 @@ class ScopedTypeRef {
object_ = object;
}
bool operator==(__unsafe_unretained T that) const { return object_ == that; }
bool operator==(T that) const { return object_ == that; }
bool operator!=(__unsafe_unretained T that) const { return object_ != that; }
bool operator!=(T that) const { return object_ != that; }
operator T() const __attribute((ns_returns_not_retained)) { return object_; }
operator T() const { return object_; }
T get() const __attribute((ns_returns_not_retained)) { return object_; }
T get() const { return object_; }
void swap(ScopedTypeRef& that) {
__unsafe_unretained T temp = that.object_;
T temp = that.object_;
that.object_ = object_;
object_ = temp;
}
......@@ -124,14 +124,14 @@ class ScopedTypeRef {
// ScopedTypeRef<>::release() is like std::unique_ptr<>::release. It is NOT
// a wrapper for Release(). To force a ScopedTypeRef<> object to call
// Release(), use ScopedTypeRef<>::reset().
T release() __attribute((ns_returns_not_retained)) WARN_UNUSED_RESULT {
__unsafe_unretained T temp = object_;
T release() WARN_UNUSED_RESULT {
T temp = object_;
object_ = Traits::InvalidValue();
return temp;
}
private:
__unsafe_unretained T object_;
T object_;
};
} // namespace base
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment