I am having a weird problem with variable values. This is the code (it is part of a class method):
MyAppDelegate *pDelegate = [[UIApplication sharedApplication] delegate];
SomeDictionaryData *appData = [pDelegate.theData retain];
NSLog(#"my instance var: %#",cardIndex); // outputs "my instance var: 4"
NSDictionary *currentCard = [[NSDictionary alloc] initWithDictionary:[appData.cards objectAtIndex:cardIndex]];;
// the above line breaks the app
[currentCard release];
[appData release];
I am using the debugger with the objc_exception_throw breakpoint. The input received by objectAtIndex in there shows as having value = 13760640. The cards attribute of appData is an NSArray and it clearly does not have ten million + items so I get an out of bounds error. I tried casting with (int)cardIndex with no better results. Weird thing is a similar code in some other class works fine.
This is some data I want to use throughout my app so I have a Model class that gets initialized in the AppDelegate as theData and then is accesed by different ViewControllers. This error shows up after one successful access on some other ViewController (that one also does retain/release).
Any help will be appreciated.
Use [cardIndex unsignedIntValue] for the objectAtIndex: line.
You cannot give objectAtIndex: a pointer because it expects an unsigned integer.
For example:
NSDictionary *currentCard = [[NSDictionary alloc] initWithDictionary:[appData.cards objectAtIndex:[cardIndex unsignedIntValue]]];
EDIT:
It sounds like cardIndex is an int but somewhere along the lines it is being set as an NSNumber instance. As a hack, use [(id)cardIndex unsignedIntValue]. If this works, then it means you are using the wrong type for cardIndex (it should be NSNumber, not int).
Related
First of all I want to point out that yes there are a lot of questions on this subject on stack overflow but none that was of any help. I also tried asking the owners of these for advice but was unable to get in touch with any of them.
Here is my scenario. I'm receiving data from an API which is an array of objects. These object are all the same structure but they change dynamically from API end point. When I made an NSArray of NSDictionary and tried to set my grid data source with the value of the provided array. It didn't work. When I looked at the documentation IGGridViewDataSourceHelper I found out the following piece of information "As of right now, the data must be of a derivation of NSObject and have at least one property". So I started thinking of a way to create an NSObject at run time. I was able to find some resource on Apple Developers documentation to make that.
Given that the variable dictionary is given in a function
Kindly check the following
- (NSArray *)getRecrodsFromDictionary: (NSDictionary*)dictionary {
// the following include the array that I want to turn into objects
NSArray * response = [self parseKey:#"responseDetails" fromDictionary:dictionary];
NSMutableArray * rows = [[NSMutableArray alloc] init];
if ([response count] != 0) {
// 1. get all NSDictionary keys
NSDictionary * temp = response[0];
NSArray * keys = [temp allKeys];
// 2. create a class
Class ModelClass = objc_allocateClassPair([NSObject class], "WidgetDetailsModel", 0);
// 3. all class variables with the same name as key retrieved from NSDictionary
for (NSString * key in keys) {
NSString * currkey = [key capitalizedString];
const char * name = [currkey UTF8String];
class_addIvar(ModelClass, name, sizeof(id), rint(log2(sizeof(id))), #encode(NSString));
}
// 4. register a class to be used
objc_registerClassPair(ModelClass);
for (NSDictionary * curr in response) {
// create object
id MC = [[ModelClass alloc] init];
for (NSString * key in keys) {
// set values
const char * name = [key cStringUsingEncoding:NSASCIIStringEncoding];
Ivar CurrVar = class_getInstanceVariable(ModelClass, name);
NSString * newValue = [curr objectForKey: key];
object_setIvar(MC, CurrVar, newValue);
}
// add object to array
[rows addObject:MC];
}
}
return [rows copy];
}
Once I get the return value and try to set it to data source data variable I get the following run time error.
[ valueForUndefinedKey:]: this class is not key value coding-compliant for the key AssetsClass.
I can't find any thing on how to make the created in runtime NSObject key value coding-compliant. How can I make it key value coding-compliant?
Edit 1:
I managed to bypass the runtime error by making the fields names capitalized.
Now the table is being populated with empty data (same number of rows as the data but empty text in it) which was the correct thing to happen because the values of the iVar is not retained. How Can I retain it?
Edit 2:
I'm still not able to retain the iVar value so I changed the location of the function to the same UIView class which then it did retain it for the short period of time I had to set the grid data source data value.
I'm curious to know if there is a way to make the iVar retained or set one of its attribute to be strong/retain to mark it for the deallocation process.
After long search on Google, StackOverFlow and other iOS related forums and research. Here is the conclusion that I was able to find. Ivar in objective-c will always be weak reference. In other words there is no way (that I can find) that makes the Ivar strong reference. This can only be achieved throw property with setting the attribute of each property made.
I basically have have a custon subclass of an UIViewController, which has a NSMutableArray called waypoints. I initialise it in the -(void)viewDidLoad method of the controller with
waypoints = [[NSMutableArray alloc] init];
Later, in a method which gets called via a presentedViewController with some parameters including a NSMutableDictionary as waypointData, I call
[waypoints insertObject:waypointData atIndex:0];
and I also tried
[waypoints addObject:waypointData];
But neither seems to work!
I logged some stuff there to make it more clear. The parameters get transmitted correctly and the NSMutableDictionary saved under waypointData is the correct content it should be. Logging the waypoints array before the insertion shows it empty (which is correct; app got launched; no data added yet) and after the insertion it's still empty. The log:
2014-02-19 14:40:11.050 xxx[xxx] waypoints before insertion: (null:)
2014-02-19 14:40:11.051 xxx[xxx] INSERT WAYPOINT
2014-02-19 14:40:11.052 xxx[xxx] waypoints after insertion: (null:)
INSERT WAYPOINT gets logged directly before the insertion, so the program routine is really executing the insertObject:atIndex: method.
TL;DR:
Even though insertObject:atIndex: (and -addObject:) for an NSMutableArray get called the object won't get inserted in the array.
EDIT:
This method gets called in -viewDidLoad too:
- (void)loadWaypoints {
id unarchivedObject = [NSKeyedUnarchiver unarchiveObjectWithFile:[[self applicationDocumentsDirectory] stringByAppendingString: kAppDataFilePlistName]];
waypoints = (NSMutableArray *)unarchivedObject;
}
unarchivedObject of course is NULL if there hasn't been anything saved yet. Thanks to 0x7fffffff.
Check your waypoints NSArray it shouldn't be nil when you call at [waypoints addObject:waypointData];
easiest way to make sure your 'waypoints' array isn't nil is doing this:
if (!waypoints)
{
waypoints = [NSMutableArray new];
}
[waypoints insertObject:waypointData atIndex:0];
or
if (!waypoints)
{
waypoints = [NSMutableArray new];
}
[waypoints addObject:waypointData];
It sounds like your waypoints array is nil. Post your header file, including the declaration of waypoints. Post your whole viewDidLoad method where you create the empty array.
The 2 most likely causes I can think of are that you declared waypoints as weak, or that you have a local variable waypoints in your viewDidLoad and you're creating an empty array in the local variable but not saving it to the instance variable.
Set a breakpoint in viewDidLoad at the line that creates the empty array. Step over it and make sure your array is being created. Then right-click on the variable down in the variables section of the debugger window and add a watchpoint to that variable. If it is getting cleared out then the breakpoint will cause your program to break at the offending line.
(My post assumes that you are using ARC. Are you?)
Please use following code:
if(waypoints == nil){
waypoints = [[NSMutableArray alloc] init];
}
[waypoints addObject:waypointData];
I've been racking my brain over this seemingly simple issue. I have a XYZObject class where I declare:
#property BOOL checked;
In my View Controller, I import the object and whenever I use 'checked', the app compiles fine but breaks at runtime wherever 'checked' is used, for example:
XYZObject *tableitem = [myDictionary[currentCategory] objectAtIndex:indexPath.row];
if (tableitem.checked) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
This was working fine until I deleted and re-added the XYZObject class, so I've been debugging under the assumption that something in the file path is what's screwing things up. But I can click on 'checked' in my VC and under Quick Help it shows the proper reference to XYZObject. This is the exact error:
[__NSCFString checked]: unrecognized selector sent to instance
EDIT/UPDATE:
With some help I've realized the issue is that when I changed my datasource from manual declaration in the ViewController, to importing a Plist, I completely scrapped my XYZObject and didn't account for it. Here is the original way I declared my dictionary:
XYZCategory *category1 = [[XYZCategory alloc]init]; category1.categoryArray = #"First Category"; [categoryArray addObject:category1];
XYZObject *object1 = [[XYZObject alloc]init]; object1.objectName = #"My String"; [objectArray addObject:object1];
myDictionary[category1.categoryArray] = objectArray;
When I switched to the Plist, the code changed to:
NSString *path = [[NSBundle mainBundle] pathForResource:#"myDictionaryPlist" ofType:#"plist"];
NSMutableDictionary *plistDictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:path];
objectArray = plistDictionary[#"First Category"];
myDictionary[category1.categoryArray] = objectArray;
And then for reference, XYZObject makes the following declarations:
#property NSString *objectName;
#property BOOL checked;
So the dictionary problem would be that I'm just pulling the direct strings for the objectArray, instead of a set of XYZObjects. I'm going to keep testing but I'm pretty sure I just have to re-define objectArray to be a set of objects based on what's pulled from the Plist.
But I also think that since I'm using the Plist now to create a dictionary (that is popped into a table where the Keys are sections and Values are rows), I can simplify things by removing the XYZCategory and XYZObject all together. Not sure if that's possible but I'm going to work towards it.
As the error message is suggesting, tableitem is actually a NSString, contrary to what you expect.
You are probably populating the dictionary in the wrong way.
I'm trying to switch views in my app using this chunk of code:
self->variable1 = [[NSNumber alloc] initWithInt:0];
self->variable2 = [[NSMutableArray arrayWithCapacity:1];
self->variable3 = [[NSMutableArray arrayWithCapacity:1];
[self presentModalViewController:titleScreen animated:YES];
If I comment out all of the allocated variable lines, the code works fine. If it leave just 1 line in the code crashes with the "EXC_BAD_ACCESS" error. Why is this happening? The variables aren't being used at all, just declared for later use. I'm not getting any compile errors on the lines either. What am I doing wrong?
UPDATE:
Thank you everyone for the help. I change the way I declare my variables to #property/#synth to clean up my code, but it didn't fix the problem. After a long time of fiddling I fixed it. I changed the code from this:
self.variable1 = [[NSNumber alloc] initWithInt:0];
to this:
self.variable1 = [NSNumber alloc];
[self.variable1 initWithInt:0];
and it worked! Can someone explain why this worked and the first line didn't?
Update:
Thank you Peter Hosey for showing me my evil ways. This time I'm pretty sure it's fixed. I was storing my variable Releases in
-(void)release
I didn't realize xCode will release when it needs to. I moved all the variable releases to
-(void)Destroy
so I can release everything on MY command. Now the code works. Thanks again!
I suggest that you declare variable1, variable2, and variable3 as properties, not instance variables. Then, use self.variable1, self.variable2, and self.variable3 to access them.
The dot syntax (self.variable1, etc.) uses the memory management policy you declared on each property; the arrow syntax (self->variable1, etc.) will access the variables directly. The crash is because you created two arrays in away that doesn't leave you owning them, and then did not assign the arrays to a property that would retain them.
You may also want to upgrade your project to use ARC. Then there is no memory-management difference; assigning to the instance variables rather than the properties will not cause the object to be prematurely released, because ARC considers instance variables to be ownerships by default. You may still want to switch to using properties after you switch to ARC, but not to prevent a crash.
In response to your edit:
I change the way I declare my variables to #property/#synth to clean up my code, but it didn't fix the problem.
Then something else was wrong.
You never did say much about the problem itself. You said you got an EXC_BAD_ACCESS, but not what statement triggered the crash or on what grounds you blamed it on the code you showed.
I changed the code from this:
self.variable1 = [[NSNumber alloc] initWithInt:0];
That's the correct code, though. That's what you should be using.
to this:
self.variable1 = [NSNumber alloc];
[self.variable1 initWithInt:0];
Noooo! That code is wrong, wrong, wrong, on multiple levels.
init methods (including initWithWhatever: methods) are not guaranteed to return the same object you sent the message to. NSNumber's initWithInt: very probably doesn't.
That object creates an uninitialized NSNumber object and assigns that to the property. Then it sends initWithInt: to that object, which will return an initialized object, which can be and very probably will be a different object. Now you are holding an uninitialized object (which you will try to use later) and have dropped the initialized object on the floor.
Never, ever, ever send alloc and init(With…) in separate expressions. Always send them in the same expression. No exceptions. Otherwise, you risk holding the uninitialized object rather than the initialized object. In your case (with NSNumbers), that is almost certainly what will happen.
What you should be doing is declaring and synthesizing a strong property that owns the NSNumber object, and creating the NSNumber object in a single statement: either [[NSNumber alloc] initWithInt:] or [NSNumber numberWithInt:]. If you're not using ARC, you'll want the latter, since the property will retain the object. If you are using ARC, they're effectively equivalent.
And if you get a crash with that code, then something else is wrong, so please tell us—either in this question or in a new question—about the crash so we can help you find the true cause of it.
variable2 and variable3 are being autoreleased before you actually access them (presumably) later after presenting the modal view.
At the very least change the lines to:
self->variable2 = [[NSMutableArray arrayWithCapacity:1] retain];
self->variable3 = [[NSMutableArray arrayWithCapacity:1] retain];
or
self->variable2 = [[NSMutableArray alloc] initWithCapacity:1];
self->variable3 = [[NSMutableArray alloc] initWithCapacity:1];
variable1 should be fine.
Best would be to use #property and #synthesize so you can use dot notation:
.h
#interface MyClass : SuperClass
#property (nonatomic,retain) NSMutableArray *variable2;
#property (nonatomic,retain) NSMutableArray *variable3;
#end
.m
#implementation MyClass
#synthesize variable2,varible3;
- (void)foo {
self.variable2 = [NSMutableArray arrayWithCapacity:1];
self.variable3 = [NSMutableArray arrayWithCapacity:1];
}
#end
By default, all instance variables in objective-c have protected scope. So unless you have explicitly declared them public in your interface file as:
#interface MYClass {
#public
NSNumber *variable1;
NSMutableArray *variable2;
NSMutableArray *variable3;
}
//...
#end
then they will not be accessible using the struct dereferencing operator. This is likely the cause of those EXC_BAD_ACCESS errors.
I have the following problem: In a certain view controller I have a NSDictionary, which itself is an entree in an NSArray object. This view controller has a child view which displays some of the key value pairs that are in this dictionary. Since I need only some key value pairs, I construct a new dictionary object from which I then remove the key value pair I do not want to have in it. To be able to access this dictionary in the child view, I though it would be possible to just set the dictionary via a property, which seems to work fine. To illustrate with some code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
...
// today is an instance of NSArray holding a number of NSDictionary objects
NSDictionary *completeData = [self.today objectAtIndex:row];
NSDictionary *data = [[NSDictionary alloc] initWithDictionary:completeData];
[data removeObjectForKey:#"name"];
SomeViewController *childController = [[SomeViewController alloc] init];
childController.data = data;
[self.navigationController pushViewController:childController animated:YES];
[childController release];
// This results in a EXC_BAD_ACCESS error when navigating back to the parent
// view and calling didSelectRowAtIndexPath a second time. When commenting this
// line out, the error dissapears, but now the object leaks
[data release];
}
The problem arises when, after returning to the parent view, I try to replace the NSArray object (today) by an updated version of itself by calling
- (void)refreshDataNotification:(NSNotification *)notification {
if (notification) {
self.today = [NSArray arrayWithArray:[[[MyAppDelegate getAppDelegate] todaySchedule]
objectForKey:#"data"]];
[self.tableView reloadData];
}
}
Note that as long as I do not release 'data' in didSelectRowAtIndexPath I get no error, but then the object leaks. When I do release it, I receive an EXC_BAD_ACCESS when refreshDataNotification is executed.
If someone has any clue as to what I might be doing wrong, then please do share with me.
Set the environment variable NSZombieEnabled to YES to get more helpful error messages about over releasing objects. (Set the environment variable by viewing details under 'Executables')
Also, it would be helpful to see how you've defined your properties. (e.g. what is the #property for data in SomeViewController?)
ps - I know you haven't pasted actual code, but data is a terrible instance name for an NSDictionary. dict is better - but something more descriptive would make your code easier to understand.