Unrecognized Selector on BOOL Property - ios

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.

Related

xcode - correct use of NSMutableDictionary

I defined in a SecondView a public property
#property (weak, nonatomic) NSMutableDictionary * ToModify;
From firseview, I used that property to passing another dicnary to dsecond view
secondView.ToModify =origin;
But when I try to modify the content of ToModify by setObject method, crash appears:
_NSCFDictionary setObject:forKey:]: mutating method sent to immutable object
It seems that ToModify is a NSDictonary, and in fact by nslog class, it appears as NSDictonary.
Why it occurs?
The compiler complains that origin is an immutable NSDictionary.
To make it mutable, call mutableCopy
secondView.ToModify = [origin mutableCopy];
Declaring an object as NSMutableDictionary isn't enough - you have to instantiate it as an NSMutableDictionary. Where are you initialising ToModify?
At some point in SecondView, you currently have
self.ToModify = [[NSDictionary alloc] init]; //or something similar
which needs to be
self.ToModify = [NSMutableDictionary dictionary];
What actually is SecondView, and what's the actual purpose of ToModify? Are you overriding an existing property, or adding a new one?

JSONModel: Filling an NSArray of generic type

I'm using JSONModel in my iOS app and i'm facing some warnings, let me explain myself a bit.
Let's say i have this JSONModel
CTVContact.h
#interface CTVContact : JSONModel
#property (nonatomic, strong) NSArray<Optional, CTVPhone> *phone;
#end
CTVContact.m
NSMutableArray *phones = [[NSMutableArray alloc] init];
for(NSString *p in personPhones) {
CTVPhone *phn = [[CTVPhone alloc] init];
phn.original = p;
[phones addObject:phn];
}
phone = [NSArray arrayWithArray:phones];
Basically it all works like a charm but i get a warning stating the following:
Incompatible pointer types assigning to 'NSArray<Optional,CTVEventParticipant> *' from 'NSArray *'
How can i go around that warning? I can't find the right way to assign all the array values to the phone Array without that warning.
Thanks a lot in advance!
NSArray<Optional, CTVPhone> *phone; defines a variable that takes an array which conforms to 2 protocols. If you try and set that a variable (phone) to an array that doesn't state that it conforms to those protocols then you will get a compile warning.
phone = [NSArray arrayWithArray:phones]; just creates a 'plain' array, with no special protocols implemented. So you get a warning.
Probably the correct thing to do is to remove the protocols from the #property definition. Unless you have an NSArray subclass which conforms to those protocols that you should actually be using...
Alternatively, and assuming that you don't try to call any methods that might be defined in those protocols:
phone = (NSArray <Optional, CTVPhone> *)[NSArray arrayWithArray:phones];
which adds a cast that basically means to the compiler: 'trust me, it's fine'...
It looks like it may be complaining on your last line, since you're passing in an NSMutableArray when NSArray's arrayWithArray method calls for an NSArray. You can get away with this by calling copy on the phones array, as such:
phone = [NSArray arrayWithArray:[phones copy]];

EXC_BAD_ACCESS error when changing views with PresentModalViewController

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.

Proper way of creating new objects which are copies of NSDictionary and NSArray objects defined in app delegate

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];

iphone nslog corrupted data

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).

Resources