Reputation: 3835
I'm having an issue where my iOS App is crashing on exit and I've narrowed it down to this:
// File.h
struct Name {
NSString *name;
}
// File.mm
Name names[] = {
@"foo",
@"bar",
};
When the App exits (pushing the home button on the iPad), I get a crash in objc_msgSend
with a EXC_BAD_ACCESS (SIGSEGV)
. It's happening the the destructor Name::~Name()
.
0 libobjc.A.dylib 0x37586e3a objc_release + 10
1 MyApp 0x0014abfc Name::~Name() (NameManager.h:21)
2 MyApp 0x0014ab42 Name::~Name() (NameManager.h:21)
3 MyApp 0x0014ad94 __cxx_global_array_dtor + 120
4 libsystem_c.dylib 0x36bba1cc __cxa_finalize + 216
5 libsystem_c.dylib 0x36b854f6 exit + 6
Keep in mind that this is in a .mm file, so it's being compiled as obj-c++.
This has worked fine for months and months. I don't know exactly when it started happening, but I suspect it was when I updated to xcode 4.4.
NSString
literals should be protected (or always have been) against multiple releases, but I don't think that is happening here anyway.
Does anyone know if something changed in xcode 4.4?
I can fix the crash by doing:
struct Name {
__unsafe_unretained NSString *name;
}
But I hate to do that without understanding why what has worked before no longer works. Or maybe it never should have worked. I also fear a memory over-write, so this might just be a symptom.
This happens in both DEBUG and RELEASE.
UPDATE: I put a breakpoint in ~Name() and verified that memory is not corrupt. LLDB is able to dump the NSString OK. But it crashes when I step.
Upvotes: 3
Views: 1400
Reputation: 43330
When you declare a "Plain Old Data" struct
in Objective-C++ that contains Objective-C objects with ARC turned on, the compiler is obligated to provide a destructor that correctly deallocates the ObjC members of the struct, even if you do not write them yourself.
This restriction does not apply in Objective-C++. However, nontrivally ownership-qualified types are considered non-POD: in C++11 terms, they are not trivially default constructible, copy constructible, move constructible, copy assignable, move assignable, or destructible. It is a violation of C++’s One Definition Rule to use a class outside of ARC that, under ARC, would have a nontrivially ownership-qualified member.
When you declared that Name structure, the compiler would have written you a destructor involving an implicit strongly held Objective-C object. At execution time, what this means is that the C++ destructor erases what it expects is a C++ structure and instead winds up borking the pointers to the string. With no valid pointer around, the -release
ARC tries to send segfaults when it tries to dereference a nonexistent receiver.
When you declare an Objective-C member __unsafe_unretained
, it tells ARC to exclude it from the destructor it would have to write, thus the struct is just destroyed, and the -release
is never sent. It is recommended that you mark all Objective-C objects in structures or classes as __unsafe_unretained
and provide the appropriate memory management yourself, because ARC can significantly complicate object lifetimes across languages.
Upvotes: 1