First, I'm very new with objective-c and memory management, pointers, etc. No doubt my problem lies in a simple point I'm missing.
I've got a class which contains a property for an integer:
// Device.H file
#interface Device : NSObject {
#private int nodeLevel;
}
#property (readwrite, assign, nonatomic) int nodeLevel;
// Device.m file
#implementation Device
#synthesize nodeLevel;
- (id)init {
self.nodeLevel = 0;
return self;
}
I create an NSMutableArray of many Device objects, assigning the node Id:
-(NSMutableArray *)getDevices {
...
NSMutableArray *devices = [[NSMutableArray alloc] initWithCapacity:[rDevices count]];
for (NSDictionary *d in rDevices) {
Device *newDevice = [[Device alloc] init] autorelease];
newDevice.nodeLevel = d.nodeLevel;
[devices addObject: newDevice];
}
return [devices autorelease];
}
My devices array is stored on the main app delegate where I've got a property assigned to hold it:
#property (nonatomic, retain) NSMutableArray *devices;
Now here's where my problem is manifest. I'm using a tableView in another controller class to access my app delegate, pull a device from its array then set values with the int, but 'strange' things happen:
EDIT: Min/Max values for the slider are set in another part of the code to 0 and 100 respectively.
// In method cellForRowAtIndex
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
Device *d = (Device *)[[appDelegate.devices objectAtIndex:indexPath.row]];
// cell is a custom cell with a UISlider object
cell.sliderLevel.value = [d nodeLevel];
When I assign value to the device's nodeLevel, the slider is always maxed out, even if nodeLevel only equals 1 or 2.
If I do this instead, the slider is at the correct position, but I'll eventually get a "EXC_BAD_ACCESS" signal when scrolling up and down through my tableView:
// cell is a custom cell with a UISlider object
cell.sliderLevel.value = [[d nodeLevel] intValue];
I suspect that I must be assigning the value to a memory location in the first instance? In the second case it works, but I assume that my BAD_ACCESS is a result of the nodeLevel becoming "released" or something? One final note, I've also got an NSString object associated with the Device class. I access the string and assign it to a label in my cell and it never causes me problems.
Thanks in advance for taking a look.
What type is returned by the nodeLevel property in this line: "newDevice.nodeLevel = d.nodeLevel;"? The nodeLevel property in Device is an int, so you need to ensure that d.nodeLevel is returning an int, and not an NSNumber object.
If d.nodeLevel is returning an NSNumber, that would explain why calling intValue on it gets you a reasonable value, and you get a huge number if you don't call intValue on it (the huge value would be the pointer value for the NSNumber object). It would also explain why you get an EXC_BAD_ACCESS crash later on, because your NSNumber object isn't being retained.
You should probably just change this line:
newDevice.nodeLevel = d.nodeLevel;
to
newDevice.nodeLevel = [d.nodeLevel intValue];
and don't call intValue on it later on, so you would change this:
cell.sliderLevel.value = [[d nodeLevel] intValue];
to this:
cell.sliderLevel.value = [d nodeLevel];
[d nodeLevel] returns an integer, a primitive type, not an Objective-C object. Therefore, you cannot call -intValue on it, and that's why you get an EXC_BAD_ACCESS.
Further, the reason your slider is maxed out is because you haven't set its maximum value to 2.0. Because it defaults to 1.0, when you set it to any value 1.0 or higher, it will appear maxed out (in your case, both 1 and 2 appear the same). At some point, you need to call cell.sliderLevel.maximumValue = 2.0; to make the maximum value possible high enough.
Related
I have an iOS app that matches incoming text fields to standard fields used to import records. My problem is that a NSMutableDictionary that uses those fields is empty! Here is the code that saves the mapping:
-(void)mapUserFields: (id) sender { // move contents of each textField when user has finished entering it
SingletonDictionary *sd = [SingletonDictionary sharedDictionary];
UITextField *tf = (UITextField *)sender; // textfield contains the pointer to user's half of the equation
int tagValue = (int)tf.tag; // get the tag value
[sd.dictionaryOfUserIndexes setObject:tf.text forKey:[NSString stringWithFormat:#"%d", tagValue]]; // value found in textField id'd by tag
NSLog(#"\nfield.text: %# tagValue: %d nsd.count: %d\n",tf.text, tagValue, sd.dictionaryOfUserIndexes.count);
}
This is the result of the NSLog:
field.text: 1 tagValue: 38 nsd.count: 0
This is the definition of the singleton in the .h file:
#property (nonatomic, retain) NSMutableDictionary *dictionaryOfUserIndexes;
This is the code to initialize the singleton in the .m file:
//-- SingletonDictionaryOfUserIDs --
+ (id) sharedDictionary {
static dispatch_once_t dispatchOncePredicate = 0;
__strong static id _sharedObject = nil;
dispatch_once(&dispatchOncePredicate, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
-(id) init {
self = [super init];
if (self) {
dictionaryOfUserIndexes = [NSMutableDictionary new];
}
return self;
}
#end
I believe my problem is because the sd.dictionaryOfUserIndexes is not initialized, but I am not sure if that's true, and if so, how to initialize it (I tried several different variants, all of which created build errors). I looked on SO and Google, but found nothing that addresses this particular issue. Help would be greatly appreciated!
There are a few things we could improve in this code, but the only thing wrong with it is the reference to dictionaryOfUserIndexes in the init method. The code as posted wouldn't compile, unless: (a) you have a line like:
#synthesize dictionaryOfUserIndexes = dictionaryOfUserIndexes;
so that the backing variable is named without the default _ prefix, or (b) you refer to the ivar with the default prefix, as in:
_dictionaryOfUserIndexes = [NSMutableDictionary new];
The other way -- preferable in most every context except within an init method -- is to use the synthesized setter, like:
self.dictionaryOfUserIndexes = [NSMutableDictionary new];
But with that change alone (so it will compile) your code runs fine, adds a value to the dictionary and logs an incremented count.
Have mercy, newbie here.
I have a NSString value that I construct on the fly, which is the name of a UILabel instance. I want to send the label a message to update its text. But, the two data types don't match. Here's enough code (I think):
In header file:
IBOutlet UILabel *Clue1; // IBOutlet and IBAction are IDE flags
IBOutlet UILabel *Clue2; // IB = interface builder
IBOutlet UILabel *Clue3;
In implementation file:
- (IBAction) newPuzzle:(id)sender { // Clear all fields & get new clue
[Clue1 setText:#""]; // Clear the fields
[Clue2 setText:#""];
[Clue3 setText:#""];
// Send up a randomly chosen new clue
NSArray *clues = [NSArray arrayWithObjects:#"222", #"333", nil];
NSInteger randomIndex = arc4random()%[clues count];
NSString *aClue = [clues objectAtIndex:randomIndex];
// The clue will be split into component digits and each piece sent to a different label
for (NSInteger charIdx = 0; charIdx < aClue.length; charIdx++) {
NSString *cluePos = [NSString stringWithFormat:#"Clue%d", charIdx + 1];
NSLog(#"%#", cluePos); // works
[cluePos setText:#"test"]; // Xcode notes the type mismatch
}
}
There are some similar questions on SO, but none are close enough for me to recognize that they apply to my case, at least as far as I can tell. Using the terminology of another language (R), I need to "coerce" the class of cluePos from NSString to UILabel. I'm on Xcode 4.2.1 and OSX 10.7.2.
TIA.
You can't coerce a string into a label because they are fundamentally different. The string doesn't have any knowledge of your view controller class or it's properties (some of which happen to be labels).
You can however use the valueForKey: method to get a property of an object by name, where the name is specified as a string. So to get a property called Clue1 on my view controller I'd say:
UILabel *label = [self valueForKey:#"Clue1"];
Or in your case, this:
NSString *cluePos = [NSString stringWithFormat:#"Clue%d", charIdx + 1];
UILabel *label = [self valueForKey:cluePos];
label.text = #"test";
(I'm assuming 'self' in this case refers to the view controller, but you can call this on any object that has properties.)
Another way to do this is to turn your string into a selector using NSSelectorFromString. That would look like this:
SEL selector = NSSelectorFromString(#"Clue1");
UILabel *label = [self performSelector:selector];
For your purposes either solution works equally well, however the advantage of using the selector is that you can pass arguments to the method call (so you could call a method that returns an object, not just access a property or IBOutlet).
Note that both of these methods will raise an exception if you try to access a property or call a method that doesn't exist. You can test if the property exists before calling it by saying:
SEL selector = NSSelectorFromString(#"Clue1");
BOOL labelExists = [self respondsToSelector:selector];
if (labelExists)
{
UILabel *label = [self performSelector:selector];
label.text = #"test";
}
else
{
//do something else
}
I am having problems adding objects to my NSMutableArray. It seems that something gets added (object count increases by 1 in debugger), but the only thing added is a 0x0 (null) instead of the address of an object. I've read through everything somewhat relevant that I could find, but I couldn't find anything that seemed to answer this issue. Most related posts seem to revolve around memory management, but the solution is not jumping out at me.
I appreciate any help you can provide.
I've included what I think are the relevant parts of the code. Please tell me if you need to see anything more.
GamePlayView.h
#interface GamePlayView : UIViewController
{
Player *gamePlayer;
NSMutableArray *boardObjects;
}
#property (retain, nonatomic) Player *gamePlayer;
#property (retain, nonatomic) NSMutableArray *boardObjects;
#end
GamePlayView.m
- (void)viewDidLoad
{
// Create player
Player *tempPlayer = [[Player alloc] initWithFrame: self.view.frame];
if (tempPlayer == NULL) {
NSLog(#"GamePlayView viewDidLoad: null Player");
}
else gamePlayer = tempPlayer;
// Create array of board objects
NSMutableArray *newArray = [[NSMutableArray alloc] init];
self.boardObjects = newArray;
[self.boardObjects addObject: gamePlayer]; // First breakpoint here
topObject = 0;
BoardObject *mine = [[BoardObject alloc] initWithFrame: self.view.frame];
[self.boardObjects addObject: mine]; // Second breakpoint here
topObject = 1;
[super viewDidLoad];
} // End viewDidLoad
I put breakpoints at the addObject lines (commented in code). When execution stops at the first breakpoint, the debugger shows a good tempPlayer, and a good gamePlayer (both with the same address). It shows 0 objects in boardObjects, like this:
boardObjects = (_NSArrayM *) 0x4b22080 0 objects
When I step over this breakpoint, the debugger shows 1 object in boardObjects, as follows:
boardObjects = (_NSArrayM *) 0x4b22080 1 objects
0 = (NSObject *) 0x0
When I continue program execution, and the debugger stops at the next breakpoint, I also see a good mine object, with boardObjects still described as above. After stepping over this breakpoint, boardObjects now looks like this:
boardObjects = (_NSArrayM *) 0x4b22080 2 objects
0 = (NSObject *) 0x0
1 = (NSObject *) 0x0
This could be the case that tempPlayer is a local variable, after returning from the function, the local variable is automatically released.
Someone suggested I display the boardObjects in the code anyway, even though it looked from the debugger that I only had null objects. I inserted this code after the last addObject:
NSLog(#"self.boardObjects is: %#", [self.boardObjects description]);
This displayed good objects: the gamePlayer and the mine!
I did verify that immediately before this NSLog statement, and immediately after, the debugger still displays two null entries in boardObjects.
It seems like the answer to my original question, then, is that the code itself is correct. However, it seems now that I have a new question: Does this mean I'll never be able to view into an NSMutableArray from within the debugger?
You should assign #property with self.
self.gamePlayer = tempPlayer;
Hi guys can somebody please advise how to cure the memory leaks in the code below
i've tried just about every combination of release and autorelease i can think of but every time either the app crashes or the leak remains
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
//get refereance to the textfield
UITextField *currentTextField = (UITextField*)[self.view viewWithTag:200];
//check which picker
if(pickerView.tag ==1)
{
// Only calls the following code if component "0" has changed.
if (component == 0) {
// Sets the global integer "component0Row" to the currently selected row of component "0"
component0Row = row;
// Loads the new values for the selector into a new array in order to reload the data.
newValues = [[NSMutableArray alloc] initWithArray:[pickerData objectForKey:[selectorKeys objectAtIndex:component0Row]]];
currentValues = newValues;
// Reloads the data of component "1".
[pickerView reloadComponent:1];
}
//run the selector logic
[self textFieldDidEndEditing:currentTextField];
}
hope someone can advise
many thanks
Your problem is these two lines:
newValues = [[NSMutableArray alloc] initWithArray:[pickerData objectForKey:[selectorKeys objectAtIndex:component0Row]]];
currentValues = newValues;
The first line allocated a new instance of NSMutableArray. The second line copies the pointer from newValues to currentValues, overwriting the pointer value in currentValues. Whatever currentValues was pointing to is lost. That's the leak.
You could fix it like this:
newValues = [[NSMutableArray alloc] init...
[currentValues release];
currentValues = newValues;
This way, whatever was pointed to by currentValues has its reference count decremented before you lose access to it.
You could also solve the problem by making currentValues an Objective-C property, and using the accessor methods via self.currentValues or [self setCurrentValues:]; those methods will handle retain/release for you.
Your NSMutableArray allocation is never released.
newValues = [[NSMutableArray alloc] initWithArray:[pickerData objectForKey:[selectorKeys objectAtIndex:component0Row]]];
You should autorelease that or release it later on when you know you don't need it anymore.
Not sure how you have currentValues defined, but this should work without leaks:
In your .h file:
#property (nonatomic, retain) NSArray * currentValues;
In your .m file:
#synthesize currentValues;
self.currentValues = newValues;
I have been struggling with the best way of creating, accessing and updating values from a dynamic boolean array for more than a week now.
#interface myDelegate : NSObject
{
NSMutableArray *aShowNote;
}
#property (nonatomic, copy) NSMutableArray *aShowNote;
This is how I have initialised my array:
NSMutableArray *aShow = [[NSMutableArray alloc] init];
for (i=0; i < c; i++)
[aShow addObject:[NSNumber numberWithBool:false]];
self.aShowNote = aShow;
This seems to work OK but I'm baffled why each element is initialised with the same address.
But then what I've discovered in my research so far is that is seems that you need to replace the object if you want to change its value:
myDelegate *appDelegate = (myDelegate *)[[UIApplication sharedApplication] delegate];
NSInteger recordIndex = 1;
NSNumber *myBoolNo = [appDelegate.aShowNote objectAtIndex:recordIndex];
BOOL showNote = ![myBoolNo boolValue];
[appDelegate.aShowNote replaceObjectAtIndex:recordIndex withObject:[NSNumber numberWithBool:showNote]];
but this approach just seems to be over complicated (and it crashes too).
Terminating app due to uncaught exception'NSInvalidArgumentException', reason: '-[__NSArrayI replaceObjectAtIndex:withObject:]: unrecognized selector sent to instance 0x5b51d00
Any pointers to improve this code (and of course to make it work) would be very gratefully received.
Thanks
Iphaaw
the problem is that copy in a property copies the assigned object. And copy creates immutable objects.
Change your property to read: #property (nonatomic, retain) NSMutableArray *aShowNote;
And I think there is not much to improve, from what I know this is the way to go if you want an NSArray with booleans.
Why not use plain C for this simple case?
BOOL *aShow = malloc(sizeof(BOOL)*c);
for (i=0 ; i<c ; i++)
aShow[i] = false;
You just have to remember to free(aShow) when you are done with it.
It is not possible to change value of a NSNumber. It not mutable class.
Then, when you ask for two same value, the same object is return.
In your array init, why you don't initialized directly the array to avoid copy problem:
aShowNote = [[NSMutableArray alloc] init];
for (i=0; i < c; i++) {
[aShowNote addObject:[NSNumber numberWithBool:false]];
}
I'm baffled why each element is initialised with the same address.
Why? NSNumbers are immutable. The runtime only needs one NSNumber object to represent FALSE.