Objective-C++中的智能指针能否完全替代ARC?
Can smart pointers in Objective-C++ completely replace ARC?
我是Cocoa框架的初学者,有一个问题:
假设我不在我的应用程序中使用 "naked" NS 指针,
那么 ARC 对我来说是多余的吗? :-)
换句话说,
以下实施是否涵盖了 ARC 的所有关注点? :-)
谢谢!
第一("alloc.."、"new.."、"copy.."或"mutableCopy.."的结果累积):
#pragma once
// In-Ref(NOP), Out-Ref(--)
template<typename T>
class NSChargePtr
{
NSChargePtr(const NSChargePtr&) = delete;
protected:
T* m_object{};
public:
NSChargePtr(T* p = __nullptr)
: m_object(p)
{
}
virtual ~NSChargePtr()
{
[m_object release];
}
operator T*()
{
return m_object;
}
operator bool()
{
return __nullptr != m_object;
}
void operator=(T* p)
{
reset(p);
}
T* get()
{
return m_object;
}
virtual void reset(T* p = __nullptr)
{
[m_object release];
m_object = p;
}
};
...第二个(本地保存,转移):
#pragma once
#include "NSChargePtr.h"
// In-Ref(++), Out-Ref(--)
template<typename T>
class NSPassPtr : public NSChargePtr<T>
{
public:
NSPassPtr(T* p = __nullptr)
: NSChargePtr<T>(p)
{
[NSChargePtr<T>::m_object retain];
}
NSPassPtr(const NSPassPtr& other)
: NSChargePtr<T>(other.m_object)
{
}
virtual void reset(T* p = __nullptr) override
{
NSChargePtr<T>::reset(p);
[NSChargePtr<T>::m_object retain];
}
void operator=(T* p)
{
NSChargePtr<T>::operator=(p);
}
};
简而言之:你可以做,但它可能不会像 "real" ARC 那样无痛,并且目前尚不清楚这种方法是否比 ARC 有任何改进。为什么你甚至想这样做?
一些我立即注意到的潜在问题:
- 您对
NSPassPtr
和 NSChargePtr
的区分已经成为潜在混淆的主要来源。您需要确切地知道在哪种情况下使用哪个,具体取决于方法 returns 是自动释放值还是保留值。您也不能干净地将自动释放表达式的结果重新分配给使用保留表达式的结果初始化的变量。
{
NSChargePtr<MyClass> obj = [[MyClass alloc] init];
// …
obj = [foo bar];
// …
} // over-release of result of [foo bar];
- 线程安全。在您的实施中,我没有看到任何防御竞争条件的证据。 ARC 在多线程上下文中具有明确定义的行为。
- 性能和编译器优化。编译器知道 ARC 并且可以在它可以证明不需要的地方省略保留和释放调用。它不能用这个实现来做到这一点。 ARC 的
objc_retain
/objc_release
可能也比发送 retain
/release
消息更快。
- 隐式转换运算符重载(
operator T*()
、operator bool
、非explicit
构造函数,例如NSChargePtr(T* p = __nullptr)
)往往是错误的来源。
我应该指出 Objective-C++ 与 ARC 兼容并且 ARC 引用在 C++ 对象 construction/destruction 的上下文中表现良好.所以通常情况下,如果你真的很难让一些代码与 ARC 一起工作,最好将它移到一个单独的代码文件中,该文件禁用了 ARC,但要为项目的其余部分启用 ARC。
最后:
NSPassPtr(const NSPassPtr& other)
: NSChargePtr<T>(other.m_object)
{
}
我觉得好像少了一个 retain
?
我是Cocoa框架的初学者,有一个问题:
假设我不在我的应用程序中使用 "naked" NS 指针, 那么 ARC 对我来说是多余的吗? :-)
换句话说, 以下实施是否涵盖了 ARC 的所有关注点? :-)
谢谢!
第一("alloc.."、"new.."、"copy.."或"mutableCopy.."的结果累积):
#pragma once
// In-Ref(NOP), Out-Ref(--)
template<typename T>
class NSChargePtr
{
NSChargePtr(const NSChargePtr&) = delete;
protected:
T* m_object{};
public:
NSChargePtr(T* p = __nullptr)
: m_object(p)
{
}
virtual ~NSChargePtr()
{
[m_object release];
}
operator T*()
{
return m_object;
}
operator bool()
{
return __nullptr != m_object;
}
void operator=(T* p)
{
reset(p);
}
T* get()
{
return m_object;
}
virtual void reset(T* p = __nullptr)
{
[m_object release];
m_object = p;
}
};
...第二个(本地保存,转移):
#pragma once
#include "NSChargePtr.h"
// In-Ref(++), Out-Ref(--)
template<typename T>
class NSPassPtr : public NSChargePtr<T>
{
public:
NSPassPtr(T* p = __nullptr)
: NSChargePtr<T>(p)
{
[NSChargePtr<T>::m_object retain];
}
NSPassPtr(const NSPassPtr& other)
: NSChargePtr<T>(other.m_object)
{
}
virtual void reset(T* p = __nullptr) override
{
NSChargePtr<T>::reset(p);
[NSChargePtr<T>::m_object retain];
}
void operator=(T* p)
{
NSChargePtr<T>::operator=(p);
}
};
简而言之:你可以做,但它可能不会像 "real" ARC 那样无痛,并且目前尚不清楚这种方法是否比 ARC 有任何改进。为什么你甚至想这样做?
一些我立即注意到的潜在问题:
- 您对
NSPassPtr
和NSChargePtr
的区分已经成为潜在混淆的主要来源。您需要确切地知道在哪种情况下使用哪个,具体取决于方法 returns 是自动释放值还是保留值。您也不能干净地将自动释放表达式的结果重新分配给使用保留表达式的结果初始化的变量。
{
NSChargePtr<MyClass> obj = [[MyClass alloc] init];
// …
obj = [foo bar];
// …
} // over-release of result of [foo bar];
- 线程安全。在您的实施中,我没有看到任何防御竞争条件的证据。 ARC 在多线程上下文中具有明确定义的行为。
- 性能和编译器优化。编译器知道 ARC 并且可以在它可以证明不需要的地方省略保留和释放调用。它不能用这个实现来做到这一点。 ARC 的
objc_retain
/objc_release
可能也比发送retain
/release
消息更快。 - 隐式转换运算符重载(
operator T*()
、operator bool
、非explicit
构造函数,例如NSChargePtr(T* p = __nullptr)
)往往是错误的来源。
我应该指出 Objective-C++ 与 ARC 兼容并且 ARC 引用在 C++ 对象 construction/destruction 的上下文中表现良好.所以通常情况下,如果你真的很难让一些代码与 ARC 一起工作,最好将它移到一个单独的代码文件中,该文件禁用了 ARC,但要为项目的其余部分启用 ARC。
最后:
NSPassPtr(const NSPassPtr& other)
: NSChargePtr<T>(other.m_object)
{
}
我觉得好像少了一个 retain
?