Roger Gilbrat
Roger Gilbrat

Reputation: 3835

iOS App crashes on exit

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

Answers (1)

CodaFi
CodaFi

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

Related Questions