Search code examples
objective-cmemory-managementmemory-leaksobjective-c++

EXC_BAD_ACCESS with NSDate in a C++ file, why?


So, I have to add a functionality to an old .cpp file.It's huge. So rewriting it in Objective C is not an option. Instead, I have added the necessary functionality using Objective-C (because I need a lot of the NSDate/NSDateFormatter functions). It worked fine. BUT, when calling the getter (on my view controller) I get this error: EXC_BAD_ACCESS.

Here is a fragment of the code:

//.h file  -----------------
// C/C++ headers
#import <Foundation/NSDate.h>
#import <Foundation/NSDateFormatter.h>

namespace MySpace {
    class Session {
        private:
            // C++ stuff
            NSDate * startTime;
        public:
            // C++ stuff
            NSDate * getStartTime();
            Session(NSDate * startTime );
    };
}

// .cpp file -----------------
using namespace MySpace;
Session:Session (NSDate * startTime) {
    // unrelated code
    if (startTime == nil ){
        startTime = [NSDate date];
    }
    setStartTime( startTime);
    // unrelated code
}

void Session::setStartTime( NSDate * startTime){
    this->startTime = [startTime copy];
}

NSDate * Session::getStartTime() {
    return this->startTime; // here I get the EXC_BAD_ACCESS
}

The entire project is compiled as Objective-C++ and ARC enabled. I believe this issue is caused because the member 'startTime' is released by ARC, and, when I call the getter, it points to nil?

How may I solve this problem?

Thanks.


Solution

  • Try that:

    NSDate * Session::getStartTime() {
        if (this == NULL) return nil;
        return this->startTime; // here I get the EXC_BAD_ACCESS
    }
    

    The change makes getStartTime immune to a NULL this pointer.

    Does that helps? If so, somewhere, you are using a dangling Session* pointer.

    Step 2

    Not that. That then:

    @interface MyNSDate: NSDate
    @end
    
    @implementation MyNSDate
    
    - (id) init
    {
        self = [super init];
        if ( self == nil ) return nil;
    
        NSLog( @"MyNSDate %@ initialized", self );
    
        return self;
    }
    
    - (void) dealloc
    {
        // ARC: no super call
        NSLog( @"MyNSDate %@ deallocated", self );
    }
    
    @end
    

    And replace the NSDate* in your class with MyNSDate. Check the messages, breakpoint in dealloc... You should be able to find out when the date is deallocated, appropriate or not, or rules out that hypothesis.

    The other idea that crossed my mind is the missing copy construcoperators. If you are copying your Session between ARC and non-ARC compilation units, it may break. You shouldn't do that, but hey, it happens.

    Session::Session( const Session& rhs )
    {
        this->startTime = [rhs.startTime copy];
    }
    
    Session& Session::operator=( const Session& rhs )
    {
        if ( this->startTime != rhs.startTime )
        {
            this->startTime = [rhs.startTime copy];
        }
    
        return *this;
    }