enumsios10nsnumber

iOS 10 NSNumber crash with enumerations


I am having a very weird issue relating to NSNumber objects for my enumerated values and access to them on a device running iOS os version 10.

Just as a disclaimer - this issue does not happen in other iOS os versions.

I have declared an enum like so:

typedef NS_ENUM(NSInteger, MYENUM) {
   FIRST = 1500,
   SECOND = 1700,
   THIRD = 1900,
   ...
};

When using this enum, I am passing it along in this fashion:

[[MyObject alloc] initObjectWith:@(FIRST)];

Excluding the inner logic, I am using the enum in a dictionary and thus need to convert it to a NSNumber.

While doing so, the application crashes, because the enum is somehow not a NSNumber, but rather a NSIndexPath.

Screenshot

Why does this happen?

When I remove the boxed literal and change the method signature to accept a NSInteger, this crash disappears.

I have tried searching online for this type of issue, but have come up short.

Further Explanation (per comment)

No special logic happens inside the init method for myObject, just assigning the property which is defined as a NSNumber to the parameter that is passed.

Regarding the crash log, Xcode is infamous in providing not so useful crash logs and all I am seeing is EXC_BAD_ACCESS, which could either mean accessing an object that has been released or a potential memory leak.

The MyObject class is defined as follows:

header file:

@interface ISNEvent : NSObject

@property(nonatomic, assign) NSNumber* number;

-(instancetype)initObjectWith:(NSNumber*)number;

@end

.m file:

- (instancetype)initObjectWith:(NSNumber*)number {
   self = [super init];
   if (self) {
     _number = number;
   }

   return self;
}

Solution

  • You have defined your property with assign memory semantics:

    @property(nonatomic, assign) NSNumber* number;
    

    That means that you’ll get a reference to whatever you supply, but you won’t keep a strong reference and you won’t nil your reference when the object is deallocated. That’s the worse of both worlds, because you’re keeping a dangling reference to an object that you’re allowing to be deallocated. As you said, this particular error “could ... mean accessing an object that has been released”, and that’s precisely what’s going on here.

    You might consider temporarily turning on zombies (command+<) or “Product” » “Scheme” » “Edit Scheme...” and go to the “Diagnostics” section of the “Run” settings, and see if your behavior changes. You’ll probably no longer see that NSIndexPath (or whatever) reference, but rather some confirmation that the NSNumber instance has been deallocated.

    Anyway, you undoubtedly meant to make this NSNumber property a strong reference:

    @property(nonatomic, strong) NSNumber *number;
    

    The other solution would be to make it weak, allowing it to be deallocated, but safely setting your reference to nil. That’s safer than assign, but I also doubt that’s what you intended. And the third alternative would be copy, which is what we sometimes use with mutable types, which isn’t applicable here.

    Bottom line, nowadays, I’d advise against ever using assign memory semantics with any object types. Use strong, copy, or weak. In this case, strong is what you want. Only use assign with primitive data types (e.g. NSInteger, CGFloat, etc.), but not with object types.

    And, remember, when you’re done testing with the zombies, turn that diagnostic feature off.