Why this strange behavior is occurring with this code? objective-c - 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.

Related

Difference between Strong and Weak references (Using ARC) Please No Theory..I know the difference theoretically

I have been trying to understand the difference between Strong and Weak references in iOS. What I did to understand is:
//.h File
#property(nonatomic,strong) NSString* myStrongString;
#property(nonatomic,weak) NSString* myWeakString;
//.m File
- (void)viewDidLoad
{
[super viewDidLoad];
[self assignTempString];
// Do any additional setup after loading the view, typically from a nib.
}
-(void)assignTempString{
self.myStrongString = [[NSString alloc] initWithString:#"Varun Mehta"];
}
- (IBAction)printAssignedString:(id)sender {
NSLog(#"Object will have strong reference so it will print my name==%#",self.myStrongString);
}
According to my understanding when I repeat the above step by using myWeakString it should print null. But its still printing my name. Anybody having any idea why its happening.
But when I replace [[NSString alloc] initWithString:#"Varun Mehta"] with [NSString stringWithFormat:#"Varun Mehta"] or [[NSString alloc] initWithFormat:#"Varun Mehta"] result is coming as I have expected.
There are several things to consider here.
A statically declared string is built into your app so it isn't really retained or released, thus a weak reference to #"my string" will always be valid. The compiler is just recognizing [[NSString alloc] initWithString:#"Varun Mehta"] as a static string and removing your alloc/init. However anything that deals with formatting is, by definition, creating a new string and thus the new string obeys the weak referencing rules and is immediately deallocated, nil-ing out the reference.
If you access a weakly retained object that ends up in the autorelease pool it won't actually get deallocated until all your methods return and the run loop goes back into another cycle (and thus drains the autorelease pool), so you can continue to work with the object even though it is "walking dead". This is typically only when interacting with non-ARC code.
If you need practise try this code:
- (void)viewDidLoad
{
[super viewDidLoad];
[self assignTempString];
}
-(void)assignTempString{
#autoreleasepool
{
self.myStrongString = [NSString stringWithFormat:#"%#", #"Strong string"];
self.myWeakString = [NSString stringWithFormat:#"%#", #"Weak string"];
}
}
- (IBAction)printAssignedString:(id)sender {
NSLog(#"Strong ptr content: %#",self.myStrongString);
NSLog(#"Weak ptr content: %#",self.myWeakString);
}
[NSString alloc] will allocate an ARC-managed object and will set its retain count to 1. As long as your view controller is alive, this retain count will be 1, so it will not be deallocated. [NSString stringWithFormat:] returns an autoreleased string which is deallocated after the execution of [self assignTempString].
Two methods initWithString and stringWithFormat suggest exactly what is to expect.
So initWithString expects you to create allocate memory and then initialise it.
While stringWithFormat expects you to just point to the string.
When you do a init with your strong/weak variable it will exist till end of your program.
While when you point;
strong literal will keep a reference and hence will not allow ARC to cleanup the string literal,
weak literal will not keep a reference and hence ARC is free to clean it up immediately after the function call.
Hope it clarifies working for you.
What you are experiencing happens because of how NSString is implemented.
Since NSString objects are immutable the compiler takes a shortcut when you use stringWithString: with a string literal as argument. If the argument of this and other related methods is a string literal the returned value will just point to the string literal. The whole object instantiation is optimized away.
And string literals won't be deallocated. But the weak variable is only nil'd out during dealloc, so if dealloc is never called the weak variables are never set to nil.
This won't happen if you use stringWithFormat:. Even using only string literals as argument will create new string instances.
Why? Most likely because Apple decided that it's not worth the effort to check if stringWithFormat: was used with a string literal that does not have any format specifiers.
That's an implementation detail, don't think too long about this decision. It should not influence the code you write. I would suggest you treat every string that is not a bare literal (i.e. #"Foo" without any NSString methods) as dynamically created NSString (i.e. use isEqualToString: for all your string comparisons)
This logging code will show this reuse behaviour. It'll show the same addresses for all NSString instances, because the compiler has optimized all those calls to a simple #"Foo".
NSLog(#"%p", #"Foo");
NSLog(#"%p", [[NSString alloc] initWithString:#"Foo"]);
NSLog(#"%p", [NSString stringWithString:#"Foo"]);
NSLog(#"%p", [[NSString stringWithString:#"Foo"] copy]);
NSLog(#"%p", [#"Foo" copy]);
In newer versions of Xcode you will even get nice warnings for this code:
using initWithString: with a literal is redundant
using stringWithString: with a literal is redundant

Objective-C handling integer values

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.

If I want to make a new instance of an object in a function whose pointer is passed by reference in it

- (void)createAString:(NSString **)str
{
*str = [NSString stringWithString:#"Hi all!"];
[*str autorelease]; // ???? is this right ?
}
How should I use release or autorelease ? I don't want to release outside of the function of course :)
...
NSString *createStr;
[self createAString:&createStr];
NSLog(#"%#", createStr);
You're correct that you'd generally want to return autoreleased (or the like) objects from out params when you use this form. Your assignment statement in the function that sets *str to a string:
*str = [NSString stringWithString:#"foo"];
is already doing the right thing, because that method returns an instance of NSString that the caller doesn't own. Just like you could return this string object from your function without any further memory management, you can set it as the outparam as you've done. Your second snippet showing the call site is fine.
This said, I'm worried about a few things in your code that you should be sure you understand:
The value of str inside the method is still a **, and sending that a message (as you've done for the speculative autorelease) is nonsense. Be sure you fully understand doubly indirected pointers before using them too liberally. :) If you need to send str a message after creating it, send it to *str, which is what contains the NSString *.
Setting an outparam like this when the function returns void is not idiomatic Cocoa. You would normally just return the NSString * directly. Outparams are rare in Cocoa. (Usually just NSErrors get this treatment from framework calls. Otherwise they conventionally use name like getString to differentiate them from normal get accessors which don't use the word "get".)
I hope -stringWithString was just an example. That method is almost never used in practice, since it's equivalent (in this case) to just using a #"string literal" (although that would muddy your example).
Instead of using a double pointer, would it not be more elegant to use an NSMutableString instead?
- (void)createAString:(NSMutableString *)str
{
[str setString:#"Hi all!"];
}
....
NSMutableString *createStr = [[NSMutableString alloc] init];
[self createAString: createStr];
NSLog(#"%#", createStr);
[createStr release];
Or, even better, just have the createAString method return an NSString.
- (NSString *)createAString
{
return #"Hi all!"; // this is autoreleased automatically
}
I wouldn't want to presume that your needs are this simple, though. =)

objective c NSString comparision

I have three button named(titled) hello, nothing, heaven and one label (IBOutlet UIlabel lab). I want to display three diff messages for three diff button click. But the following code failed to accomplish this. Can anyone suggest any idea?
-(IBAction)buttonclick:(id)sender
{
NSString *title=[sender titleForState:UIControlStateNormal];
if([title isEqualToString:#"hello"])
{
NSString *str=[[NSString alloc] initWithFormat:#"abc"];
}
else if([title isEqualToString:#"nothing"]) {
NSString *str=[[NSString alloc] initWithFormat:#"def"];
}
else if([title isEqualToString:#"heaven"])
{
NSString *str=[[NSString alloc] initWithFormat:#"ijk"];
}
lab.text=str;
[str release];
}
output:
warning:unused variable str;
Don't use the title of the buttons to differentiate between your buttons. It wouldn't work if your buttons were to be localised. Either use different actions, or use the tag to differentiate them.
The warning is the clue to what you're doing wrong in this case. A local variable is only visible within the scope that it's declared, so your lab.text=str line is actually setting lab.text to a str that is defined elsewhere, either a static variable or an instance variable. Here's what you could do instead:
NSString *str;
switch ([sender tag]) {
case FirstButtonTag:
str = #"abc";
break;
case SecondButtonTag:
str = #"def";
break;
case ThirdButtonTag:
str = #"ijk";
break;
}
lab.text = str;
The problem is that in each 'then' clause of the various if statements, you're creating a new local variable named str, assigning it to a new string, and then the variable goes out of scope. The compiler warning should tick you off to this: you're writing to a variable but never reading from it.
Ordinarily, your code wouldn't compile, but you apparently have another variable named str in scope later on. Your new definitions of str are shadowing the old one: while the new name str is in scope, the name str refers to that variable, not the outer one, and the outer one is cannot be referred to.
The solution is to move the declaration of str up to the top of the function. Furthermore, it's simpler just to use [NSString stringWithFormat:#"blah"] instead of [[NSString alloc] initWithFormat:#"blah"], since the former gives you an autoreleased object. This saves you from having to manually release it later on. Note that assigning lab.text=str retains it, since the text property of the UILabel class has the retain modifier.
-(IBAction)buttonclick:(id)sender
{
NSString *title=[sender titleForState:UIControlStateNormal];
NSString *str;
if([title isEqualToString:#"hello"])
{
str=[NSString stringWithFormat:#"abc"];
}
else if([title isEqualToString:#"nothing"])
{
str=[NSString stringWithFormat:#"def"];
}
else if([title isEqualToString:#"heaven"])
{
str=[NSString stringWithFormat:#"ijk"];
}
lab.text=str;
}
Also note that with your original code, you had both a memory leak and memory corruption -- since you were allocating a string and then losing a reference to it (by the new local variable str going out of scope) without releasing it, and then you were calling release an extra time on whatever the outer str variable was. Moving the str declaration to the top of the function fixes both problems.
I'm also assuming that your format strings are more complicated than just plain strings. If you're actually assigning constant strings such as "abc", then of course it's much simpler to just do str=#"abc" instead of str=[NSString stringWithFormat:#"abc"].

Is if (variable) the same as if (variable != nil) in Objective-C

I am getting a EXC_BAD_ACCESS (SIGBUS) on this line in my iPhone project:
if (timeoutTimer) [timeoutTimer invalidate];
The thing that has me stumped is that I don't understand how that line could crash, since the if statement is meant to be checking for nil. Am I misunderstanding the way Objective-C works, or do line numbers in crash statements sometime have the wrong line in them?
Just because a variable is set to a value other than nil doesn't mean it's pointing to a valid object. For example:
id object = [[NSObject alloc] init];
[object release];
NSLog(#"%#", object); // Not nil, but a deallocated object,
// meaning a likely crash
Your timer has probably already been gotten rid of (or possibly hasn't been created at all?) but the variable wasn't set to nil.
I just ran into a similar issue, so here's another example of what might cause a check such as yours to fail.
In my case, I was getting the value from a dictionary like this:
NSString *text = [dict objectForKey:#"text"];
Later on, I was using the variable like this:
if (text) {
// do something with "text"
}
This resulted in a EXC_BAD_ACCESS error and program crash.
The problem was that my dictionary used NSNull values in cases where an object had an empty value (it had been deserialized from JSON), since NSDictionary cannot hold nil values. I ended up working around it like this:
NSString *text = [dict objectForKey:#"text"];
if ([[NSNull null] isEqual:text]) {
text = nil;
}
They should be the same. Perhaps the line number is in fact incorrect.
Look for other possible errors near that in your code and see if you find anything.