我正在使用一个遗留代码库,其中包含大量使用手动保留/发布编写的Objective-C++。内存使用大量的C++std::shared_ptr<NSMyCoolObjectiveCPointer>
进行管理,在构造中传递一个合适的删除器,在包含的对象上调用release
。这似乎效果很好;但是,在启用 UBSan 时,它抱怨指针未对齐,通常是在取消引用shared_ptrs以执行某些工作时。
我已经搜索了线索和/或解决方案,但很难找到关于Objective-C对象指针来龙去脉的技术讨论,甚至很难找到任何关于Objective-C++的讨论,所以我在这里。
这是一个完整的Objective-C++程序,演示了我的问题。当我使用 UBSan 在 Macbook 上运行它时,我在shared_ptr::operator*
中遇到未对齐的指针问题:
#import <Foundation/Foundation.h>
#import <memory>
class DateImpl {
public:
DateImpl(NSDate* date) : _date{[date retain], [](NSDate* date) { [date release]; }} {}
NSString* description() const { return [&*_date description]; }
private:
std::shared_ptr<NSDate> _date;
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
DateImpl date{[NSDate distantPast]};
NSLog(@"%@", date.description());
return 0;
}
}
我在给DateImpl::description
的电话中得到这个:
runtime error: reference binding to misaligned address 0xe2b7fda734fc266f for type 'std::__1::shared_ptr<NSDate>::element_type' (aka 'NSDate'), which requires 8 byte alignment
0xe2b7fda734fc266f: note: pointer points here
<memory cannot be printed>
我怀疑使用&*
将shared_ptr<NSDate>
"投射"到NSDate*
有些不对劲.我想我可以通过在shared_ptr上使用.get()
来解决这个问题,但我真的很好奇发生了什么。感谢您的任何反馈或提示!
这里有一些红鲱鱼:shared_ptr
、手动保留/释放等。但我最终发现,即使是这个非常简单的代码(启用了 ARC(也会导致 ubsan 命中:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSDate& d = *[NSDate distantPast];
NSLog(@"%@", &d);
}
return 0;
}
这似乎只是[NSDate distantPast]
的问题(顺便说一下,[NSDate distantFuture]
,但不是,例如,[NSDate date]
(。我的结论是,这些必须是在 Foundation 深处的某个地方粗略/未对齐地分配的单例对象,当您取消引用它们时,它会导致指针读取未对齐。
(请注意,当代码只是NSLog(@"%@", &*[NSDate distantPast])
时,它不会发生。我认为这是因为编译器只是将原始指针上的&*
折叠为无操作。它不适用于原始问题中的shared_ptr
情况,因为shared_ptr
过载operator*
.鉴于此,我相信在纯 Objective-C 中没有简单的方法可以做到这一点,因为您无法将&
操作与*
操作分开,就像涉及C++引用时所做的那样[通过将*
的临时结果存储在NSDate&
中]。
您不应该使用"裸"NSDate
类型。Objective-C 对象应始终与指向对象的指针类型一起使用(例如NSDate *
(,并且您永远不应该获得"指针后面的类型"。
特别是在64位平台上,Objective-C对象指针有时可能不是有效的指针,而是"标记指针",它将对象的"值"存储在指针的某些位中,而不是作为实际分配的对象。你必须始终让 Objective-C 运行时机制处理 Objective-C 对象指针。将其取消引用为常规 C/C++ 指针可能会导致未定义的行为。