[K/N] Constructor of ObjectPtr with retain ^KT-63423

This commit is contained in:
Alexander Shabalin
2024-01-10 12:28:36 +01:00
committed by Space Team
parent c4992f1912
commit e5269daa74
3 changed files with 74 additions and 1 deletions
@@ -20,12 +20,16 @@ OBJC_FORWARD_DECLARE(NSObject);
namespace kotlin::objc_support {
struct object_ptr_retain_t{};
inline constexpr object_ptr_retain_t object_ptr_retain{};
namespace internal {
class ObjectPtrImpl {
public:
ObjectPtrImpl() noexcept;
explicit ObjectPtrImpl(NSObject* object) noexcept;
ObjectPtrImpl(object_ptr_retain_t, NSObject* object) noexcept;
ObjectPtrImpl(const ObjectPtrImpl& rhs) noexcept;
ObjectPtrImpl(ObjectPtrImpl&& rhs) noexcept;
@@ -51,6 +55,7 @@ public:
void reset() noexcept;
void reset(NSObject* object) noexcept;
void reset(object_ptr_retain_t, NSObject* object) noexcept;
bool operator==(const ObjectPtrImpl& rhs) const noexcept;
bool operator<(const ObjectPtrImpl& rhs) const noexcept;
@@ -65,8 +70,10 @@ private:
// `std::shared_ptr`-like smart pointer for ObjC objects.
//
// IMPORTANT: Must be constructed from a retained ObjC object.
// IMPORTANT: By default constructed from a retained ObjC object.
// This optimizes for the common case of constructing directly from `[[T alloc] init...]`.
// To construct and additionally retain the object, use `object_ptr_retain` as the first argument
// to the constructor.
//
// Unlike a regular C++ smart pointer `operator*` does not return a reference but return a pointer
// for convenient syntax of calling ObjC methods: `[*smartPtr methodName]`.
@@ -88,6 +95,9 @@ public:
// IMPORTANT: this does not `retain` `ptr`.
explicit object_ptr(T* ptr) noexcept : impl_(ptr) {}
// Construct from ObjC object, additionally retaining `ptr`.
object_ptr(object_ptr_retain_t, T* ptr) noexcept : impl_(object_ptr_retain, ptr) {}
void swap(object_ptr<T>& rhs) noexcept { impl_.swap(rhs.impl_); }
T* get() const noexcept { return (T*)impl_.get(); }
@@ -104,6 +114,9 @@ public:
// IMPORTANT: this does not `retain` `ptr`.
void reset(T* ptr) noexcept { impl_.reset(ptr); }
// Release stored pointer and store `ptr` instead, additionally retaining `ptr`.
void reset(object_ptr_retain_t, T* ptr) noexcept { impl_.reset(object_ptr_retain, ptr); }
template <typename U>
bool operator==(const object_ptr<U>& rhs) const noexcept {
return impl_ == rhs.impl_;
@@ -15,6 +15,8 @@ objc_support::internal::ObjectPtrImpl::ObjectPtrImpl() noexcept : object_(nil) {
objc_support::internal::ObjectPtrImpl::ObjectPtrImpl(NSObject* object) noexcept : object_(object) {}
objc_support::internal::ObjectPtrImpl::ObjectPtrImpl(object_ptr_retain_t, NSObject* object) noexcept : object_([object retain]) {}
objc_support::internal::ObjectPtrImpl::ObjectPtrImpl(const ObjectPtrImpl& rhs) noexcept : object_([rhs.object_ retain]) {}
objc_support::internal::ObjectPtrImpl::ObjectPtrImpl(ObjectPtrImpl&& rhs) noexcept : object_(rhs.object_) {
@@ -51,6 +53,10 @@ void objc_support::internal::ObjectPtrImpl::reset(NSObject* object) noexcept {
}
}
void objc_support::internal::ObjectPtrImpl::reset(object_ptr_retain_t, NSObject* object) noexcept {
reset([object retain]);
}
std::size_t objc_support::internal::ObjectPtrImpl::computeHash() const noexcept {
return object_.hash;
}
@@ -85,6 +85,21 @@ TEST(ObjectPtrTest, ObjectCtor) {
testing::Mock::VerifyAndClearExpectations(&destructorHook);
}
TEST(ObjectPtrTest, ObjectCtorWithRetain) {
testing::StrictMock<testing::MockFunction<DestructorHook>> destructorHook;
auto* ptr = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
{
objc_support::object_ptr<WithDestructorHookObjC> obj(objc_support::object_ptr_retain, ptr);
EXPECT_CONTAINS(obj, ptr);
EXPECT_CALL(destructorHook, Call(ptr.impl)).Times(0);
}
testing::Mock::VerifyAndClearExpectations(&destructorHook);
EXPECT_CALL(destructorHook, Call(ptr.impl));
[ptr release];
}
TEST(ObjectPtrTest, CopyCtor) {
testing::StrictMock<testing::MockFunction<DestructorHook>> destructorHook;
@@ -304,6 +319,24 @@ TEST(ObjectPtrTest, ResetObjectFromEmpty) {
EXPECT_CALL(destructorHook, Call(ptr.impl));
}
TEST(ObjectPtrTest, ResetObjectFromEmptyWithRetain) {
testing::StrictMock<testing::MockFunction<DestructorHook>> destructorHook;
auto* ptr = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
{
objc_support::object_ptr<WithDestructorHookObjC> obj;
obj.reset(objc_support::object_ptr_retain, ptr);
EXPECT_CONTAINS(obj, ptr);
EXPECT_CALL(destructorHook, Call(ptr.impl)).Times(0);
}
testing::Mock::VerifyAndClearExpectations(&destructorHook);
EXPECT_CALL(destructorHook, Call(ptr.impl));
[ptr release];
}
TEST(ObjectPtrTest, ResetObjectFromSet) {
testing::StrictMock<testing::MockFunction<DestructorHook>> destructorHook;
@@ -319,6 +352,27 @@ TEST(ObjectPtrTest, ResetObjectFromSet) {
EXPECT_CALL(destructorHook, Call(ptr2.impl));
}
TEST(ObjectPtrTest, ResetObjectFromSetWithRetain) {
testing::StrictMock<testing::MockFunction<DestructorHook>> destructorHook;
auto* ptr1 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
auto* ptr2 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
{
objc_support::object_ptr<WithDestructorHookObjC> obj(ptr1);
EXPECT_CALL(destructorHook, Call(ptr1.impl));
obj.reset(objc_support::object_ptr_retain, ptr2);
testing::Mock::VerifyAndClearExpectations(&destructorHook);
EXPECT_CONTAINS(obj, ptr2);
EXPECT_CALL(destructorHook, Call(ptr2.impl)).Times(0);
}
testing::Mock::VerifyAndClearExpectations(&destructorHook);
EXPECT_CALL(destructorHook, Call(ptr2.impl));
[ptr2 release];
}
#define EXPECT_EQUAL(expr1, expr2) \
do { \
EXPECT_TRUE((expr1) == (expr2)); \