Objective-C handling integer values - objective-c

I am getting confused with how to handle Integers in Objective C.
If I define the following:
NSInteger i = 6;
NSLog(#"%d", i);
I expect it to print 6 to the console.
however I have an NSInteger within an object which is obviously reference by a pointer so I get very difference results.
For example:
#interface Section : NSObject {
NSInteger Id;
}
#property (nonatomic, assign) NSInteger Id;
Please assume this has been synthesized in the implementation.
I create the object set its value and access it again as follows:
Section *section = [[Section alloc] init];
section.Id = 6;
NSMutableArray *sections = [[NSMutableArray alloc] init];
[sections addobject:section];
Section *sectionB = [setions objectAtIndex:0];
NSLog(#"%d", sectionB.Id);
This has the strange effect of printing the memory address ie a number like 5447889. Why can I not just get the value?
I have tried using:
NSInteger sid = [[section Id]integerValue];
But I then get the warning Invalid receiver type 'NSInteger' and sometime get an error Program received signal: “EXC_BAD_ACCESS”.
I would really like to know how to handle Integers, or any values for that matter properly.
Many Thanks

It looks like you're accessing uninitialized memory; 5447889 doesn't look like a pointer value—pointers are usually word-aligned, and 5447889 isn't word aligned.
Maybe you could cut and paste your actual code. This has typos such as addobject instead of addObject and setions instead of sections.
Does it work if you keep things simple and do NSLog(#"%d", section.Id) to skip messing with the array?

Regarding the strange values, see Dominic Cooney's answer.
Regarding the EXC_BAD_ACCESS: [[section Id]integerValue]; doesn't work because it then tries to interpret the NSInteger as an object and tries to send the message integerValue to it, which can't work. It's an integral number, not an object.

Think I found the answer to my own question. Setting the value of an NSInteger to a value of say 6 is fine. The problem I have is I am setting it to the value returned from a Json string using the following:
NSInteger i = [jsonResult objectForKey:#"Id"];
which should be:
NSInteger i = [[jsonResult objectForKey:#"Id"] integerValue];
I have not tested this but makes sense based on what DarkDust said about integerValue taking an object and not an Integer.
Thanks for your input.

Related

Playing around with ARC: Force release irritation?

I am currently playing around with ARC a bit to get some things figured out, before starting to do the actual work. I did setup this code:
NSNumber* n = [[NSNumber alloc] initWithInt:3];
__weak NSNumber* weakN = n;
n = nil;
NSLog(#">>>: %# %#", n, weakN);
I expected n and weakN to be nil, as n = nil; should trigger a release in my eyes? Unfortunately it doesn't. The output is ">>>: (null) 3". What am I missing here?
Another thing is, that I am pretty sure, the below code was giving me a hard time when starting with arc:
__weak NSNumber* weakN2 = [[NSNumber alloc] initWithInt:3];
NSLog(#">>>: %#", weakN2);
I am pretty sure, I've had some problems with similar code, as arc would release the object straight after initialization, as there is no strong reference to the object. Unfortunately, the output of the above is ">>>: 3".
It would be great to get some clarification on this stuff. I am clearly missing something here!
Best regards,
Michael
In addition to what kevboh said, it's also rather pointless to create weak references to simple immutable Foundation objects like NSNumber. For performance reasons, Foundation might well vend you a cached object instead of creating an entirely new one. And if it doesn't now, then it might in some future release.
The upshot is that you're probably not the sole owner of the object returned by [[NSNumber alloc] initWithInt:3], no matter what you think.
Well, you just picked a bad object to test this with. If you do it with NSString's (or most other objects), you get the expected result:
NSString* n = [[NSString alloc] initWithFormat:#"3"];
__weak NSString* weakN = n;
n = nil;
NSLog(#">>>: %# %#", n, weakN);
// Output is (null) (null)
__weak NSString* weakN2 = [[NSString alloc] initWithFormat:#"3"];
NSLog(#">>>: %#", weakN2);
// Output is (null)
The behavior of NSNumber is caused because the class is caching the number that was created so is actually still valid. The same behavior will be exhibited if you use string constants that are compiled in as part of the code. (Like NSString* n = #"3";)
I expected n and weakN to be nil, as n = nil; should trigger a release in my eyes? Unfortunately it doesn't. The output is ">>>: (null) 3". What am I missing here?
ARC doesn't work like that. Ownership of the object is nondeterministic; ARC likely held on to it until the end of your function. You should not expect deallocs to happen and instead use strong/weak references when you intend ownership to happen.

What's 'void' on NSArray?

I defined a NSArray in a header file like this:
#property (nonatomic, retain) NSArray *ages;
In my implementation I want to set this variable like this:
ages = [self setAges:[ageValues allKeys]];
'ageValues' is a NSDictionary. So what I do is just setting the array of keys to my self-defíned array. Strange enough, I get the following error message:
Asssigning to 'NS Array *' from incompatible type 'void'
But where can I find something void here? In my opionion I am just setting another array ([ageValues allKeys) to my own array and I can't find anything void???
The setAges: method is a method that returns void, in other words: it returns nothing (not even nil or something; it literally is not returning anything). Now you cannot assign "nothing" to a variable.
That being said, your code wants to do the same thing twice. All you want to do is simply:
self.ages = [ageValues allKeys];
or:
[self setAges:[ageValues allKeys]];
They do exactly the same, but use different syntax (the compiler transforms the first into the second).
setAges is a void method since it's a setter. As such, it returns void and you're then trying to assign it to your ages member. All you need to do is call setAges.
[ self setAges:[ ageValues allKeys ] ];
self.ages = [ageValues allKeys];
or
ages = [[ageValues allKeys] retain];
[self setAges:[ageValues allKeys]]; returns void.
If you have synthesized your ages property in your .m like so:
#synthesize ages;
The setter is automatically generated for you, so all you need to do is
self.ages = [ageValues allKeys];

Why this strange behavior is occurring with this code? objective-c

I have a method (the code below is a simplified version) that parses small text files:
- (void)parseFile:(NSString *)aFile
{
NSDate *date;
NSNumber *number;
NSString *desc;
NSString *txt = [NSString stringWithContentsOfFile:aFile encoding:NSUTF8StringEncoding error:nil];
for (NSString *line in [txt componentsSeparatedByString:#"\n"]) {
if ([linesubstring isEqual:#"mydate"]) {
date = [dateFormat dateFromString:strDate];
}
if ([linesubstring isEqual:#"mynumber"]) {
number = [numberFormat numberFromString:strValue];
}
if ([linesubstring isEqual:#"mydesc"]) {
desc = [line substringWithRange:NSMakeRange(0, 10)];
}
if (!date && !number && !desc) {
...do something...
}
}
}
The first problem is that date variable is being filled with the content of aFile parameter. It only assumes it's correct value, when the passes through the fist if/check.
So why? I though that date could be a reserved word and exchanged it, but with the same behavior.
The second problem is with the last if (with the nested ones). Debuging the code, i can see that xcode shows it as "out of scope", but !number fails (xcode thinks that it's valid)...
I tried other combinations, like [number isNotEqualTo:[NSNull null]] (this one throws an error EXC_BAD_ACCESS), without success.
Please, could anybody give some hints? I'm newbie with cocoa/objective-c. I'm coming from java...
TIA,
Bob
There's quite a few things wrong with the code you've provided. I'm using the answer box because there isn't enough room for this to be a comment:
With regards to your variable declarations:
NSDate *date;
NSNumber *number;
NSString *desc;
You have correctly declared them, but you have not initialised them. As they are, they could be pointing to any random garbage. This means that your test at the end of the loop…
if (!date && !number && !desc) {
...do something...
}
…may in fact always execute because date, number and desc may always be non-zero (I say may because it is actually undefined whether they are zero or non-zero). Initialise each of them to nil if you plan to determine whether they are set or not:
NSDate *date = nil;
NSNumber *number = nil;
NSString *desc = nil;
It is not always necessary to initialise variables (for example, as long as you write to it before you read from it, it is not necessary to initialise it), however some people promote the idea of initialising all variables to prevent this undefined behaviour from surfacing (I typically initialise all variables even if I overwrite the initialised value anyway).
Also, there is a variable called linesubstring but it is not declared anywhere in the code, similarly strDate, strValue are not declared anywhere either. It is important to know how these are declared and how these are used as they may similarly be pointing to garbage.

Objective C: What is the best way to create and use a dynamic boolean array?

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.

One problem of NSMutableArray

the code is:
typedef struct _Package
{
char* data;
int dataLen;
}Package;
Package *pack=(Package *)malloc(sizeof(pack));
pack->dataLen = 10;
pack->data = (char *)malloc(10);
strcpy(pack->data,"hellohello");
NSMutableArray *lstPack = [[NSMutableArray alloc] init];
[lstPack addobjec:pack];
when the program goto [lstPack addobject:pack],it cann't go on.
If you know the reason,please tell me。
Thank you!
You can add to obj-c containters (including NSMutableArray) only obj-c objects. To add a c-structure to array you can wrap it to NSValue object:
[lstPack addObject:[NSValue valueWithPointer:pack]];
Later you access stored value:
Package* pack = (Package*)[[lstPack objectAtIndex:i] pointerValue];
Note also that you possibly have a typo in that line - method name is incorrect.
“… the result is that the p->data is nil …” — perhaps because of pack->dataLen = (char *)malloc(10);
I think, you wanted to do pack->data = (char *)malloc(10); instead?
Greetings
You can create a CFMutableArray instead which can handle arrays of arbitrary objects, and you can use it as you would an NSMutableArray (for the most part).
// create the array
NSMutableArray *lstPack = (NSMutableArray *) CFArrayCreateMutable(NULL, 0, NULL);
// add an item
[lstPack addObject:pack];
// get an item
Pack *anObject = (Pack *) [lstPack objectAtIndex:0];
// don't forget to release
// (because we obtained it from a function with "Create" in its name)
[lstPack release];
The parameters to CFArrayCreateMutable are:
The allocator to use for the array. Providing NULL here means to use the default allocator.
The limit on the size of the array. 0 means that there is no limit, any other integer means that the array is only created to hold exactly that many items or less.
The last parameter is a pointer to a structure containing function pointers. More info can be found here. By providing NULL here, it means that you don't want the array to do anything with the values that you give it. Ordinarily for an NSMutableArray, it would retain objects that are added to it and release objects that are removed from it¹, but a CFMutableArray created with no callbacks will not do this.
¹ The reason that your code is failing is because the NSMutableArray is trying to send retain to your Pack struct, but of course, it is not an Objective-C object, so it bombs out.