I am using a NSArray to store the names of pictures that I want to use in a UITableViewCell. When I pull the table view too much to scroll the app crashes and gives me:
Thread 1: EXC_BAD_ACCESS (code=2, address=0xc).
This is what I'm using to initialize my Array:
In my .h:
#property (retain, nonatomic) NSArray *imagesToDisplay;
In my .m:
(viewDidLoad)
_imagesToDisplay = #[#[#"one.png", #"two.png", #"three.png"],
#[#"four.png", #"five.png", #"six.png"],
#[#"seven.png", #"eight.png", #"nine.png"],
#[#"ten.png", #"eleven.png", #"twelve.png"],
#[#"thirteen.png", #"fourteen.png", #"fifteen.png"],
#[#"sixthteen.png", #"seventeen.png", #"eighteen.png"]
];
This is what I'm doing to retrieve the image and set the image view as those images:
int row = [indexPath row];
cell.image1.image = [UIImage imageNamed:self.imagesToDisplay[row][0]];
cell.image2.image = [UIImage imageNamed:self.imagesToDisplay[row][1]];
cell.image3.image = [UIImage imageNamed:self.imagesToDisplay[row][2]];
Even when I comment out the different cell.image lines they still give me the thread 1 error. My specific error is
Thread 1: EXC_BAD_ACCESS (code=2, address=0xc).
I'm new to objective c so anything helps. Thank you in advance!
I'm not sure if this is merely an exercise in multi-dimensional arrays but I would recommend against this.
Your first step should be to create a proper data model (class object) for your table. For example:
#interface ImageRow : NSObject
#property (nonatomic, copy) NSArray *imagePaths;
- (id)initWithArray:(NSArray *)array;
- (NSString)imagePathAtIndex:(NSUInteger)index;
#end
Then you would store an NSArray of ImageRows instead of the multi-dimensional array. Fundamentally, you are accomplishing the same thing but it's both more clear and more extensible.
Your crash EXC_BAD_ACCESS is typically when you try to access an object that is nil when you're expecting it not to be. In most cases, Obj-C will handle it gracefully but there are times where it does not.
The crash is because you are not retaining an autoreleased object. When you say a property is retain, the memory management is only done for you if you use its setter, typically by using self.property =. You are instead accessing the backing ivar directly. Because array literals are autoreleased and you have failed to retain it, the array is autoreleased and _imagesToDisplay points to garbage. Add a retain call or use its setter to fix this.
Also SiLo is right about the data model.
Related
I have 2 objects in an NSMutableArray one is a CBPeripheral object and the other is an NSString.
Here you can see that the NSMutableArray contains the two objects:
Then, in line 1249, I try to assign the 'periph' object to a CBPeripheral and in line 1252 I try to assign the 'ADVTname' to a text label.
Neither line 1249 nor 1252 is the correct syntax for what I am trying to do (line 1252 won't even compile).
Can someone tell me how to access my objects in my 'thisrow' NSMutableArray?
Thanks,
Dale
EDIT #1: I am trying to understand how 'thisrow' has become an NSDictionay but I am stumped, here are all the code snippets that put the objects into 'thisrow'. Perhaps there is a flaw in the whole idea I have that is trying to remember the ADVTname for all peripherals that got added to _foundbleHalos.
//AFTER #interface foundblehalos was defined
#property (strong,nonatomic) NSMutableArray* foundbleHalos;
//in (void)centralManager > didDiscoverPeripheral
[_foundbleHalos addObject:#{#"periph": peripheral, #"ADVTname": ADVRTperipheralName}]; //2022-b70-3
//when I try to populate a TableView with all the ADVTnames we found:
NSMutableArray *thisrow = [_foundbleHalos objectAtIndex:indexPath.row];
blecell.textLabel.text = [NSString stringWithFormat:#"%#",thisrow.lastObject];
CBPeripheral* HaloForRow = (CBPeripheral*) thisrow[0];
EDIT #2: Well I think this following code is the answer to my topic question but I do not understand it since I do not know why 'thisrow' turned out to be a dictionary type when I originally defined it as an NSMutableArray:
//AFTER #interface foundblehalos was defined
#property (strong,nonatomic) NSMutableArray* foundbleHalos;
//in (void)centralManager > didDiscoverPeripheral
[_foundbleHalos addObject:#{#"periph": peripheral, #"ADVTname": ADVRTperipheralName}]; //2022-b70-3
//when I try to populate a TableView with all the ADVTnames we found:
NSDictionary *thisrow = [_foundbleHalos objectAtIndex:indexPath.row];
blecell.textLabel.text = thisrow[#"ADVTname"];
CBPeripheral* HaloForRow = (CBPeripheral*) thisrow[#"periph"];
I came across this scenario where my app was crashing with exec_bad_access when I call a block on a nil object. I was able to fix the issue by add a if condition but I want to know why calling block on a nil object causes bad_access?
#interface CustomView :UIView
#property (nonatomic, strong) UIImage* sourceImage;
#property (nonatomic, copy) void(^doneSwipingBlock)();
- (void)testMethod;
#end
//Another Class
//Sample Code (this is not the actual code but shows the crash
CustomView view = nil;
view.sourceImage = [UIImage imageNamed:#"image.png"]; //no error as view is nil
[view testMethod]; //no error as view is nil
view.doneSwipingBlock(); //Crashes here
/*
//This works fine
if (view.doneSwipingBlock) {
view.doneSwipingBlock();
}
*/
Because while blocks are objects, operations on blocks should not be perceived as respecting Objective-C semantics. Just like reading from NULL causes a bad access, invoking a NULL block causes a bad access. This is consistent with the behavior of a function pointer (calling a NULL function pointer crashes your program), and since blocks are available in C and C++ as well, it may have made more sense to use this behavior than a purely Objective-C behavior.
The invoke method is not called through objc_msgSend, which handles and cancels calls to nil objects on Objective-C objects.
Blocks are variables, not methods. You can expect the same things out of a block property than you can out of an int property. They're just like you had some class called Block that had an invoke method on it, and you subclassed it every time you needed it to do something different, except that the compiler does all the subclassing part.
I have a Singleton object that manages all my lists. We'll call it ListStore.
ListStore has a mutable array, which stores Lists.
#interface ListStore : NSObject
#property (nonatomic, copy) NSMutableArray *lists; // an array of List objects
end
Lists has a mutable array, which stores Things.
#interface Wanderlist : NSObject <NSCoding, NSCopying>
#property (nonatomic, copy) NSMutableArray *things; // an array of Thing objects
#end
At any time, a background process might go through ListStore and loop through and process all Lists, while a user might be interacting with a List.
To guard against "object was mutated while being enumerated" type errors, I do this:
// all of this is in a background thread
NSArray *newLists = [[ListStore sharedStore] lists] copy];
for (List *list in newLists) {
// yay, no more crashes, because I'm enumerating over a copied object, so the user
// can do whatever they want while I'm here
for(Thing *thing in list.things) {
// oh crap, my copy and the original object both reference the same list.things,
// which is why i'm seeing the 'mutation while enumerating" errors still
...
}
}
I originally thought that because I made a copy into newLists that all of its members would be properly copied. I now understand that not to be the case: I'm still seeing the "object was mutated while enumerated" errors, but this time it's happening on list.things.
Can I use NSCopying with my setup so that when I say:
[[ListStore sharedStore] copy];
It calls copyWithZone: on Lists, so I can then copyWithZone: on things?
I tried to set it up like this but copyWithZone: wasn't getting called.
I know I could simply say NSArray *newList = [list.things copy] but I'd like to get a better understanding of NSCopying at the very least.
Right before submitting this question I clicked on a question in SO's list of related questions, and found my solution.
Figured it doesn't hurt to post my solution.
Instead of this:
NSArray *newLists = [[ListStore sharedStore] lists] copy];
I had to do:
NSArray *newLists = [[NSArray alloc] initWithArray:[[ListStore sharedStore] lists] copyItems:true];
From the NSArray docs:
- (id)initWithArray:(NSArray *)array copyItems:(BOOL)flag
flag:
If YES, each object in array receives a copyWithZone: message to create a copy of the object—objects must conform to the NSCopying protocol. In a managed memory environment, this is instead of the retain message the object would otherwise receive. The object copy is then added to the returned array.
Once I used initWithArray:copyItems:, it automatically sent copyWithZone to all my List objects, and I was able to then manually perform a copyWithZone on list.things.
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 am wondering what the correct way is to make a copy of an object defined in the app delegate or a singleton object. In short, I am making an app which requires a user to login. This login view is just a modal view controller on top of the 'real' app, which consists of a tabbarcontroller, plus some tableview controllers. After a successful login, there is send a data request to a remote server, and the modal view controller is dismissed, revealing the tabbar controller and table views holding the XML data. To parse the incoming data, I have created a singleton object named DataParser, which has interface
...
#interface DataParser : NSObject {
// Data objects that hold the data obtained from XML files
NSMutableDictionary *personnel;
NSMutableDictionary *schedule;
NSMutableDictionary *today;
}
#property (nonatomic, retain) NSMutableDictionary *personnel;
#property (nonatomic, retain) NSMutableDictionary *schedule;
#property (nonatomic, retain) NSMutableDictionary *today;
...
Now in these dictionaries I store (mutable) dictionaries and arrays holding NSString objects with the parsed XML data. Since I do not want to modify these original objects holding the parsed data (that is to say, I only want to modify them at the login stage, but not in any of the tableview controllers), I am creating a new dictionary object which holds a copy of the content of one of the dictionaries above in each tableview controller. So for instance, in the loadView of a view controller called ScheduleViewController I have
...
#interface ScheduleViewController : UITableViewController {
NSDictionary *copyOfSchedule;
}
#property (nonatomic, retain) NSDictionary *copyOfSchedule;
...
#end
#implementation ScheduleViewController
#synthesize copyOfSchedule;
- (void)loadView {
[super loadView];
DataParser *sharedSingleton = [DataParser sharedInstance];
self.copyOfSchedule = [NSDictionary dictionaryWithDictionary:sharedSingleton.schedule];
}
...
Now this seems to work fine. The only difficulty arises however, when the user 'logs out', which entails popping the login modal view controller back on the stack. When the user presses the login button again, then a new XML data request is send to the server and the dictionaries in the singleton object get refreshed with the (new) data (I check if they contain any data, if so I call removeAllObjects before filling them up again with newly parsed data). At this point the dictionaries in all view controllers should be updated too, however I am not quite sure how to go about this the right way. I have noticed that loadView is not always called again in this case and so to this end I have added the same code as above in loadView to every viewWillAppear method. After navigating back and forth between the different views or navigating back and forth between child views of a tableview a couple of times, I receive an EXC_BAD_ACCESS error however. I suspect this has to do with not properly retaining the copies of the original dictionaries, but I don't seem to be able to find a solution around this. Instead of using dictionaryWithDictionary, which I suspect is not the right way to go anyway, I also tried a different approach, where instead of using objects of type NSDictionary in ScheduleViewController I use NSMutableDictionary. So:
...
#interface ScheduleViewController : UITableViewController {
NSMutableDictionary *copyOfSchedule;
}
#property (nonatomic, retain) NSMutableDictionary *copyOfSchedule;
...
#end
#implementation ScheduleViewController
#synthesize copyOfSchedule;
- (void)loadView {
[super loadView];
DataParser *sharedSingleton = [DataParser sharedInstance];
self.copyOfSchedule = [[NSMutableDictionary alloc] initWithDictionary:sharedSingleton.schedule];
}
- (void)viewWillAppear {
DataParser *sharedSingleton = [DataParser sharedInstance];
[self.copyOfSchedule removeAllObjects];
[self.copyOfSchedule addEntriesFromDictionary:sharedSingleton.schedule];
[self.tableView reloadData];
}
...
But this doesn't get rid of the EXC_BAD_ACCESS errors. To make a very long story short: what would be the best way to go about making independent copies of objects defined in a singleton object or app delegate and which can be dynamically updated at request? Since I am already rather into the project and lots is going on, I realize that my question may be a bit vague. Nonetheless I hope there is somebody who could enlighten me somehow.
Deep copies are often made recursively. One way to do it would be to add -deepCopy methods to NSDictionary and NSArray. The dictionary version might go like this:
- (NSDictionary*)deepCopy
{
NSMutableDictionary *temp = [self mutableCopy];
for (id key in temp) {
id item = [temp objectForKey:key];
if ([item respondsToSelector:#sel(deepCopy)] {
// handle deep-copyable items, i.e. dictionaries and arrays
[temp setObject:[item deepCopy] forKey:key]
}
else if ([item respondsToSelector:#(copy)]) {
// most data objects implement NSCopyable, so will be handled here
[temp setObject:[item copy] forKey:key];
}
else {
// handle un-copyable items here, maybe throw an exception
}
}
NSDictionary *newDict = [[temp copy] autorelease];
[temp release]
return newDict;
}
I haven't tested that, so be a little careful. You'll want to do something similar for NSArray.
Note that views are not copyable.
It is quite a typical pattern that you build an array or dictionary with some code, so clearly it must be mutable while you add bits to it, and when you're done you don't want it ever to change. To do this:
Have a property like
#property (...) NSArray* myArray;
When you calculate the contents of myArray, use a mutable array to build it, like
NSMutableArray* myMutableArray = [NSMutableArray array];
When you're done building the array, just use
self.myArray = [NSArray arrayWithArry:myMutableArray];