Mac OS X, Objective-C crash with NSMutableArray - objective-c

Environment: Mac OS 10.8.5, XCode 5.1.1
Problem: Crash in obj_msgsend on addObject message to a NSMutableArray
Disclaimer: I'm new to Objective-C, so this could an obvious mistake. But it's mysterious.
Details:
I've been able to prune the problem down to a small test case (thankfully), though the exact manifestation of the problem is different from the full application.
Here's the #interface:
#interface ObjCQueue : NSObject
+ (void) push: (NSString *)calEvent;
+ (NSString *) pop;
#end
Here's the Objective-C class implementation.
#import <Foundation/Foundation.h>
#include "ObjcQueue.h"
NSMutableArray *qArray;
#implementation ObjCQueue
{
}
+ (void) init
{
qArray = [[NSMutableArray alloc] init];
// NSLog(#"(init)qArray class is: %#\n", NSStringFromClass([qArray class]));
}
+ (void) push:(NSString *)calEvent
{
[qArray addObject:calEvent];
}
+ (NSString *) pop
{
// This will return nil if there's no first object
NSString *retEvent = [qArray objectAtIndex:0];
// Don't delete the front of the queue if nothing is there
if (retEvent != nil)
[qArray removeObjectAtIndex:0];
return retEvent;
}
#end
and main.m does this:
int main(int argc, const char * argv[])
{
#autoreleasepool {
[ObjCQueue init];
[ObjCQueue push:#"Pushed thing"];
NSLog(#"Popped: %#\n", [ObjCQueue pop]);
}
return 0;
}
For the moment, let's ignore the possibility that how I'm doing this is totally wrong (we'll get back to that).
If I run this code as-is, I get a crash in objc_msgSend called by the addObject message sent from [ObjCQueue push:]
The mystery part is, if I uncomment the NSLog call in [ObjCQueue init] everything runs just fine.
In the larger application, I see a different issue. The failure also occurred in the push method, except the run-time error I got said that addObject was an invalid selector. When I check the type of qArray in that case, it has a type of NSDictionary (that's from memory, it wasn't spelled exactly that way) instead of NSMutableArray. Also, in the larger application, adding the NSLog call in the init method makes everything run smoothly.
In this smaller example, the type of qArray always appears to be NSMutableArray.
In other answers to similar questions, the implication is that the object corresponding to qArray is getting overwritten, and/or released prematurely. I don't see how that could happen here, since it's global, and ObjCQueue only has class methods and no instance of it is created. [ObjCQueue init] is only called once.
One other bit of data: In this smaller case, qArray gets displayed differently depending where (in the debugger) it's displayed.
In init, in the case where it crashes, immediately after qArray gets its value, the debugger shows:
Printing description of qArray:
<__NSArrayM 0x10010a680>(
)
But in push, just before the addObject method is called, the debugger shows:
Printing description of qArray:
(NSMutableArray *) qArray = 0x000000010010a680
The value is the same, but the type is kinda sorta different (maybe). In the case with no crash, the display is identical in both cases (they're both the same as the first display)
This may not be the best way (or it may be a blatantly wrong way) to initialize qArray, and I can accept that. But why would the behavior change with the addition of the NSLog call?
Any help/insights will be appreciated.
-Eric
P.S. Here's the XCode project: Bug Test

The problem is because ARC is releasing qArray before you called push so you're calling on an object that is already released. A good solution to this problem would be to either change your class to an actual instance, or create a singleton so that ARC knows to retain the array rather than just releasing it right after you init.

Related

Objective-C method parameter type-safety

If I have methods like:
- (BOOL)isValidRow:(NSDictionary*)contentVersionRow
do we really have to continually check like this at the beginning of the method
if(![contentVersionRow isKindOfClass:[NSDictionary class]]) {
// Handle unusual situation - probably return NO in this case
}
to really implement proper type-safety inside Objective-C methods? Because in theory the parameter is not guaranteed to point to an NSDictionary object, is this correct?
EDIT: So answers so far seem to indicate we should not check for this, but then what is the difference between checking for this and checking for nil parameter, which I assume we should do? Or should we not check for nil either, if it's not normally expected? Both cases cover the situation of a misbehaving caller.
Just like in C you are dealing with pointers in Objective-C. So saying NSDictionary * simply means "here's a pointer to a memory address that contains an instance of NSDictionary".
Example:
#import <Foundation/Foundation.h>
#interface Test : NSObject
- (void)useDictionary:(NSDictionary *)dictionary;
#end
#implementation Test
- (void)useDictionary:(NSDictionary *)dictionary
{
NSLog(#"Keys: %#", [dictionary allKeys]);
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
Test *test = [[Test alloc] init];
// 1: This works fine
[test useDictionary:#{#"key": #"value"}];
// 2: This will cause a compiler warning (or error depending on the options passed to the compiler)
[test useDictionary:#"not a dictionary"];
// 3: This will compile without any warnings
[test useDictionary:(NSDictionary *)#"not a dictionary"];
}
}
The 2nd and 3rd examples will cause the program to crash at runtime. So if you want to pass incorrect things to methods, you can. Usually Xcode will warn you if you have a type-mismatch.
Update about nil-checking: If it's an API-misuse to pass nil to your method, then throw an exception. That's what exceptions are for in Objective-C: to catch programming mistakes, not to handle expected runtime issues (like an unreachable network). If your method can just silently fail if nil is passed in or handle it in a sensible way, then do that instead. For example if you have a method addValue:(NSNumber *)number that adds the given value to a sum then it wouldn't be a big deal if someone called it with nil: Just don't add anything :)
Yes, but you shouldn’t.
Obj-C is a dynamic language, so it is up to each object to determine if it responds to a certain method. It is bad style to check the class of an object.
Instead, if you want to check that an object supports a selector you should use -respondsToSelector:, but only if you handle objects not responding to that selector.

Bad Access Parent is Null - Wht is this Happening?

I understand what the error is, but in this case not what is causing it. In general use it occurs maybe 1% of the time (probably less) but I have found an extreme way to cause it which I will describe below. First, I am using an in-app purchase process I found on Ray Wenderlich's site. Below are the specific pieces of concern here:
.h:
typedef void (^RequestProductsCompletionHandler)(BOOL success, NSArray * products);
#interface IAPHelper : NSObject
- (void)requestProductsWithCompletionHandler:RequestProductsCompletionHandler)completionHandler;
#end
.m
#implementation IAPHelper
{
SKProductsRequest * _productsRequest;
RequestProductsCompletionHandler _completionHandler;
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSLog(#"Loaded list of products...");
_productsRequest = nil;
NSArray * skProducts = response.products;
for (SKProduct * skProduct in skProducts) {
NSLog(#"Found product: %# %# %0.2f",
skProduct.productIdentifier,
skProduct.localizedTitle,
skProduct.price.floatValue);
}
_completionHandler(YES, skProducts); // here is where bad access occurs
_completionHandler = nil;
}
Again, 99%+ of the time this works just fine. Given how infrequent the bad access happens in regular use and it has been difficult to diagnose. However, I found an extreme way to cause the issue. The setup is "Tab 1" is a table view controller and "Tab 2" is a table view controller that uses the code from above. If I quickly switch back and forth between the two tabs I can usually cause the problem to occur anywhere from a few seconds into it to 20-30 seconds. Doesn't happen every time in this scenario but it does the vast majority. As marked above the following line gets a bad access error with Parent is Null.
_completionHandler(YES, skProducts);
To solve the issue I simple do the following:
if (_completionHandler)
{
_completionHandler(YES, skProducts);
_completionHandler = nil;
}
While that fix does work and does solve the problem I am still bothered by why this is occurring. Any thoughts as to the cause of this?
Update:
Apologies to all as I did forget to include the following in what I pasted above.
- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler {
// 1
_completionHandler = [completionHandler copy];
// 2
_productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers];
_productsRequest.delegate = self;
[_productsRequest start];
}
You need to treat your completion block as any other object when you are storing it. So if you are storing your block as a variable and then using it within a different scope from where you assign it, you need to increment the reference count by either copying it or retaining it. The simple solution is to create a strong property to store your block.
Depending on unseen bits of code, your completion handler block might not be being assigned correctly. You need to copy a block if you intend to use it outside of the scope in which it was created.
In your interface, declare your completion handler's storage attribute as "copy".
#property (nonatomic, readwrite, copy) void (^completionHandler)(BOOL, NSArray *);
If you want to control the local variable, you can synthesize the property manually in your implementation:
#synthesize completionHandler = _completionHandler;

In objective c can a class essentially delete itself?

In objective c, suppose I have an object Obj stored in a NSMutableArray, and the array's pointer to it is the only strong pointer to Obj in the entire program. Now suppose I call a method on Obj and I run this method in another thread. In this method, if Obj sets the pointer for itself equal to nil will it essentially delete itself? (Because there will be no more strong pointers left) I suspect the answer is no, but why? If this does work, is it bad coding practice (I assume its not good coding, but is it actually bad?)
It is highly unlikely that an object would be in a position to cause its own release/deallocation if your code is designed properly. So yes, the situation you describe is indicative of bad coding practice, and can in fact cause the program to crash. Here is an example:
#interface Widget : NSObject
#property (retain) NSMutableArray *array;
#end
#implementation Widget
#synthesize array;
- (id)init
{
self = [super init];
if(self) {
array = [[NSMutableArray alloc] init];
[array addObject:self];
}
return self;
}
- (void)dealloc
{
NSLog(#"Deallocating!");
[array release];
[super dealloc];
}
- (void)removeSelf
{
NSLog(#"%d", [array count]);
[array removeObject:self];
NSLog(#"%d", [array count]);
}
#end
and then this code is in another class:
Widget *myWidget = [[Widget alloc] init];
[myWidget release]; // WHOOPS!
[myWidget removeSelf];
The second call to NSLog in removeSelf will cause an EXC_BAD_ACCESS due to the fact that array has been deallocated at that point and can't have methods called on it.
There are at least a couple mistakes here. The one that ultimately causes the crash is the fact that whatever class is creating and using the myWidget object releases it before it is finished using it (to call removeSelf). Without this mistake, the code would run fine. However, MyWidget shouldn't have an instance variable that creates a strong reference to itself in the first place, as this creates a retain cycle. If someone tried to release myWidget without first calling removeSelf, nothing would be deallocated and you'd probably have a memory leak.
If your back-pointer is weak (which it should be since a class should never try to own it's owner, you will end up with a retain-cycle) and you remove the strong pointer from the array the object will be removed from the heap. No strong pointers = removed from memory.
You can always test this.
If you need a class to bring to a situation where its deleted, the best practice is to first retain/autorelease it and then make the situation happen. In this case the class won't be deleted in a middle of its method, but only afterwards.
I think we can say it might be bad coding practice, depending on how you do it. There are ways you could arrange to do it safely, or probably safely.
So let's assume we have a global:
NSMutableArray *GlobalStore;
One approach is to remove yourself as your final action:
- (void) someMethod
{
...
[GlobalStore removeObject:self];
}
As this is the final action there should be no future uses of self and all should be well, probably...
Other options include scheduling the removal with a time delay of 0 - which means it will fire next time around the run loop (only works of course if you have a run loop, which in a thread you may not). This should always be safe.
You can also have an object keep a reference to itself, which produces a cycle and so will keep it alive. When its ready to die it can nil out its own reference, if there are no other references and that is a final action (or a scheduled action by another object) then the object is dead.

NSMutableDictionary Singleton issue

I am coding Objective-C using the Cocos2D framework, and I have a singleton used for multiple purposes. One new purposes is to get and set character's "states" which are strings. I've recently made an NSDictionary for this purpose, but I have issues with the program freezing up when a method inside the singleton is called.
Here's the singleton code. I'm just leaving in the character state stuff:
.h
#interface ExGlobal : NSObject {
NSArray *charStates_keys;
NSArray *charStates_objects;
NSMutableDictionary *charStates;
}
#property(nonatomic, retain) NSMutableDictionary *charStates;
+(ExGlobal*)sharedSingleton;
- (NSString *)charState:(NSString *)charName;
- (void)set_charState:(NSString *)value forCharName:(NSString *)charName;
#end
.m
#import "ExGlobal.h"
#implementation ExGlobal
#synthesize charStates;
static ExGlobal* _sharedSingleton = nil;
+(ExGlobal*)sharedSingleton {
#synchronized([ExGlobal class]) {
if (!_sharedSingleton) {
[[self alloc] init];
}
return _sharedSingleton;
}
return nil;
}
+(id)alloc {
#synchronized([ExGlobal class]) {
NSAssert(_sharedSingleton == nil, #"Attempted to allocate a second instance of a singleton.");
_sharedSingleton = [super alloc];
return _sharedSingleton;
}
return nil;
}
-(id)init {
self = [super init];
if (self != nil) {
// initialize stuff here
exitName = #"ruinsSkyMid";
sceneChangeKind = #"reborn";
charStates = [[NSMutableDictionary alloc] init];
charStates_keys = [NSArray arrayWithObjects:#"Feathers", #"Hummus", nil];
charStates_objects = [NSArray arrayWithObjects:#"at wall", #"with Feathers", nil];
charStates = [NSMutableDictionary dictionaryWithObjects:charStates_objects forKeys:charStates_keys];
}
return self;
}
- (NSString *)charState:(NSString *)charName{
NSString *value = [charStates objectForKey:charName];
return value;
}
- (void)set_charState:(NSString *)charState forCharName:(NSString *)charName{
[charStates setObject:charState forKey:charName];
}
- (void)dealloc {
//I know it doesn't get called, but just in case
[charStates release];
[super dealloc];
}
#end
It's unclear to me what exactly the issue is when it freezes. When this happens, all I get in the console is:
Program received signal: “EXC_BAD_ACCESS”.
warning: Unable to read symbols for /Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.3.5 (8L1)/Symbols/Developer/usr/lib/libXcodeDebuggerSupport.dylib (file not found).
Previous frame inner to this frame (gdb could not unwind past this frame)
Previous frame inner to this frame (gdb could not unwind past this frame)
Which I'm sure doesn't help finding the issue. I found if I redefine charStates_keys, charStates_objects and charStates inside both the charState and set_charState methods, it seems to work without freezing, except set_charState does not change the state.
It isn't freezing, it is crashing. Hence the EXC_BAD_ACCESS. It looks like your Xcode installation is borked, too, as the two messages following should not happen.
Note that methods should not have _s in the name; not a cause of the problem, but a comment on following convention.
You aren't retaining charStates and that is likely the cause of the crash.
Not an answer as such but I didn't have enough space in the comments field above to post this, but it might be useful.
As bbum already said, your lack of retaining charStates is likely the problem.
If you are confused about when to retain and not retain objects there's a really good book called "Learn Objective-C on the Mac" and I know it's a Mac book but most of it applies to iPhone too. On page 171 of chapter 9 (Memory Management) it talks about the "Memory Management Rules" and how if you are confused about when to retain or not then you don't understand the simple rules of Objective C memory management.
Essentially if you create an object using new, alloc or copy, then the retain count is automatically set to 1 so the object is retained and does not require you to retain it and will require a subsequent release to deallocate.
If you create the object any other way then the object will be an autoreleased object.
Obviously these rules only apply within the standard iOS libraries and can't necessarily be applied to third party libraries.
I recommend anyone who doesn't fully understand memory management in Objective C read this book. I found highly enlightening even for my iPhone work.
Hope that helps/.

When is an autoreleased object actually released?

I am new in objective-c and I am trying to understand memory management to get it right.
After reading the excellent
Memory Management Programming Guide for Cocoa by apple my only concern is when
actually an autoreleased object is released in an iphone/ipod application. My understanding is at the end of a run loop. But what defines a run loop in the application?
So I was wondering whether the following piece of code is right. Assume an object
#implementation Test
- (NSString *) functionA {
NSString *stringA;
stringA = [[[NSString alloc] initWithString:#"Hello"] autorelease]
return stringA;
}
- (NSString *) functionB {
NSString *stringB;
stringB = [self functionA];
return stringB;
}
- (NSString *) functionC {
NSString *stringC;
stringC = [self functionB];
return stringC;
}
- (void)viewDidLoad {
[super viewDidLoad];
NSString* p = [self functionC];
NSLog(#"string is %#",p);
}
#end
Is this code valid?
From the apple text I understand that the NSString returned from functionA is valid in the scope of functionB. I am not sure whether it is valid in functionC and in viewDidLoad.
Thanks!
Yes, your functions are valid, and return objects using correct Cocoa conventions for retain/release/autorelease/copy.
To answer your question about what the runloop is, in your application's main() function, it invokes UIApplicationMain(). You can imagine UIApplicationMain looks something like this:
void int UIApplicationMain (int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName) {
UIApplication *app = /* create app using principalClassName */;
[app setDelegate:/* create delegate using delegateClassName */];
while (![app shouldTerminate]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
event = [app getNextEvent];
[app dispatchEvent:event];
[pool drain];
}
}
That while loops is similar to what the UIKit is actually doing, and each trip through that while loop is like a trip through the runloop, where the function getNextEvent blocks waiting for some event to happen. All of your methods are typically called from within something like dispatchEvent:. You might try setting a break point in one of your methods, like an IBAction, and looking in the debugger call stack way up at the top to see the names of the UIKit methods that handle the events and runloop. Since each of your methods are called from within that while loop, each time you call autorelease on an object, that object is added to that outter pool in the run loop. When the current event is finished being dispatched, the pool is drained, and those objects are finally sent release messages.
One last note. There can be more than one autorelease pool, that aren't always at the end of the event loop. Sometimes you might allocate tens of thousands of objects in one trip thorough the event loop. When that happens, you might setup additional inner auto release pools in your own methods to keep the number of autoreleased objects in autorelease pools down. Auto release pools can stack.
There's nothing wrong with that code. It will compile and run as you expect.
The NSString object returned from functionA is still valid upon return since it's being passed down the stack to the next guy (functionB) who is now keeping track of it.