objective c NSString comparision - objective-c

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"].

Related

Modify parameters in Objective-C blocks

I would like to modify various variables which exist outside an Objective-C block within it's body.
I know I can directly access and modify a variable using the __block attribute while declaring the variable. So this works:
__block NSMutableString *alertMessage;
void(^appendMessage)(NSMutableString*, NSString*)= ^(NSString *append){
if (!alertMessage)
{
alertMessage = [NSMutableString new];
}
if ([append length] > 0)
{
[alertMessage appendString:#"\n"];
}
[alertMessage appendString:append];
};
appendMessage(#"Alert part 1"); //This works fine
However I want to create a block which can perform an operation on a passed variable, enabling me to use the operation on multiple variables outside the block without directly accessing the same. Something like the following:
__block NSMutableString *alertMessage;
__block NSMutableString *otherString;
void(^appendMessage)(NSMutableString*, NSString*)= ^(NSMutableString *string, NSString *append){
if (!string)
{
string = [NSMutableString new];
}
if ([append length] > 0)
{
[string appendString:#"\n"];
}
[string appendString:append];
};
//The following do not work as intended
appendMessage(alertMessage, #"Alert Part 1");
appendMessage(otherString, #"Bleh bleh");
I want to be able to use the above block to modify the variables declared before it.
How can I achieve such an operation? Is this even possible?
Your question shows some confusion over values and variables, maybe the following will help.
Modify parameters in Objective-C blocks
In (Objective-)C all parameters to methods/functions/blocks are passed by value, e.g. when in the call f(x) the value of the variable x is passed to f, not the variable itself. This is known as call-by-value.
There are languages which do allow variables to be passed, known as call-by-reference. When used the argument must be a variable and the parameter name within the function is effectively an alias to the supplied variable. This is not supported directly in (Objective-)C.
However you can emulate it in (Objective-)C. It is not commonly used, with one notable exception: many methods use it to return an NSError * value.
You later comment:
What I want to achieve includes object creation, which is essentially what the question now boils down to. "Can I create an object declared outside within a block?". The answer which I have gathered with the help of all the activity here is NO.
You can, it is just a question of whether you should (i.e. is the design right?) and the best way to do it.
The straightforward way to solve your particular issue is to write a function:
NSMutableString *alertMessage;
NSMutableString *otherString;
NSMutableString *(^appendMessage)(NSMutableString *, NSString *) =
^(NSMutableString *string, NSString *append)
{
if (!string)
string = [NSMutableString new];
if (append.length > 0)
{
[string appendString:#"\n"];
[string appendString:append];
}
return string;
};
alertMessage = appendMessage(alertMessage, #"Alert Part 1");
otherString = appendMessage(otherString, #"Bleh bleh");
If you really (really, really) want to you can instead "pass the variable" by passing its address (using the & operator) and indirection (using the * operator) inside the block to get/set the value:
void (^appendMessage)(NSMutableString **, NSString *) =
^(NSMutableString **stringPtr, NSString *append)
{
if (!stringPtr) return; // no "variable" passed
NSMutableString *string = *stringPtr; // use indirection to get the value in the passed variable
if (!string)
string = [NSMutableString new];
if (append.length > 0)
{
[string appendString:#"\n"];
[string appendString:append];
}
*stringPtr = string; // use indirection to set the passed variable
};
appendMessage(&alertMessage, #"Alert Part 1"); // pass "variable" by passing its address
appendMessage(&otherString, #"Bleh bleh");
While the above is valid code it is generally not recommended coding practice in Objective-C for simple cases such as yours.
Once you take the address of a variable you need to be concerned over the lifetime of that variable - if you attempt to use the address to access the variable after the variable has been destroyed your program will fail (the dangling pointer problem)
What about __block?
Neither of the above examples use __block anywhere.
When a block references a variable by default it captures the variables value at the time the block is created. The __block attribute changes this to capturing the variable (so its value can be changed by the block) and alters the lifetime of the capture variable if required (so the variable lives at least as long as the capturing block, avoiding the dangling pointer problem).
The __block attribute is not applicable in your situation as you wish to capture different variables based on the call.
HTH
The code, as written, seems to confuse operation on object with object creation.
For clarity's sake, you should either pass in a mutable object to be manipulated or you should define a single __block variable whose value will be set by the block (and you do the logic after to figure out where that value should be stuffed).
Passing in something by reference is inherently dangerous to the point of being an anti-pattern (what happens as soon as you try to refactor the code to be asynchronous? At least in the __block case, the code after the block will see nil).
i.e.:
__block NSMutableString *foo = [sourceString mutableCopy];
doIt(#"new stuff"); // appends to `foo`
whereItShouldReallyGo = foo;

Make variable accessible outside conditional?

About a year ago, when I first got into programming, I learned the hard way that variables don't escape the scope of the condition they're created in. For example:
-(void)someMethod {
if (x == y) {
NSString *string = [[NSString alloc] initWithString:#"Foo"];
NSLog(string); // outputs "Foo" to console successfully
}
...
NSLog(string); // Doesn't work, we're out of the scope of the "if" statement.
}
My question is, is there any way to dynamically create variables within a conditional statement and be able to access them at other times, kind of like if I declared it in my #interface?
EDIT I guess I didn't explain it well, I meant if I wanted to use it later in other methods.
You just need to declare (and optionally initialize) the variable outside of the if. So something like:
-(void)someMethod {
NSString *string = nil;
if (x == y) {
string = [[NSString alloc] initWithString:#"Foo"];
NSLog(string); // outputs "Foo" to console successfully
}
...
NSLog(string);
}
EDIT
To respond to your clarification, 'string' here is a local variable, local to this method. You have other options like instance variables for example. Instance methods like this one (ones that start with '-') will have access to this instance's (this object's) instance variables.
So you could do:
#interface MyClass : NSObject {
NSString *string; // <<-- This is an instance variable (aka "ivar")
}
- (void)someMethod;
- (void)someOtherMethod;
#end
#implementation MyClass
- (void)someMethod {
string = #"Foo";
}
- (void)someOtherMethod {
NSLog (string);
// will print "Foo" provided someMethod was called before this method
}
#end
Obviously there's more to this than you can get in a short answer. You can have global variables. If you're new to the language, you should read properties as a very useful tool for encapsulating instance variables (useful when you want to get the memory mgmt right). But hopefully that gets you pointed in the right direction at least.
-(void)someMethod {
NSString *string = nil;
if (x == y) {
string = [[NSString alloc] initWithString:#"Foo"];
NSLog(string); // outputs "Foo" to console successfully
}
...
NSLog(string); // Doesn't work, we're out of the scope of the "if" statement.
}
Consider what you're asking. You start with:
-(void)someMethod
{ if (x == y)
{ NSString *string = [[NSString alloc] initWithString:#"Foo"];
NSLog(string); // outputs "Foo" to console successfully
}
...
NSLog(string); // Doesn't work, we're out of the scope of the "if" statement.
}
What would you like the second NSLog to do? You seem to be requesting that it either work, if the body of the if statement has been executed, or what? Produce a dynamic "undeclared variable" error?
Now you seem to be wanting:
-(void)someMethod
{ if (x == y)
{ NSString *string = [[NSString alloc] initWithString:#"Foo"];
NSLog(string); // outputs "Foo" to console successfully
}
...
}
-(void)someOtherMethod
{
NSLog(string); // Doesn't work, we're out of the scope of the "if" statement.
}
What do you wish to happen in someOtherMethod if the body of the if statement in someMethod has not been executed?
As #Daniel's solution points out, you can reference a variable provided it is in scope. In the single method case you move the point of declaration out of the if and into the enclosing method body. In the two method case you move it to the class - as an instance variable.
Now scope isn't the same as lifetime - a variable can exist (be alive), but no be accessible (it is out of scope); the common example is when one method calls another, the calling method's variables stay alive but are inaccessible to the called method.
In a similar way that a variable is in scope does not mean that it has to contain a valid value. That is why #Daniel puts nil in the variable - you know if a value has been created inside the body of the if by the value of string not being nil.
So maybe this is the "dynamic" behaviour you seem to be seeking - you must declare some way to reference the value (the variable), but you indicate whether it has been created by storing some sentinel (nil in this case) in the variable.

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 's NSString subStringToIndex causing strange issue

Hi everyone I am working on a game and I have run into a really weird issue and was hoping to get some help. Basically sometimes the text is a bit long to show in one textbox so I wanted it to break down a string and store the undisplayed text in a string to be used in the next message box. So when I use this code..
NSString * Talkin1 = #"Hello I am an annoying string";
NSString * Talkin2 = [Talkin1 substringToIndex:5];
It makes Talkin2 the value of Hello which is what I want. Then I store it in the object with..
[_window setMultiMessage:Talkin2];
which goes to my set and get methods in an object i made.
In it's interface...
NSString * multiMessage;
in its .m
-(void) setMultiMessage:(NSString*)messageTwo
{
multiMessage = messageTwo;
}
-(NSString*) getMultiMessage
{
return multiMessage;
}
then later the main object pulls it out again, when it is done closing the first window with...
NSString * talking = [_window getMultiMessage];
Now in debugging, I have noticed talking's value will be "out of scope" when i get the string back from _window. So then it crashes my program when it tries to use it.
But. If i do this everything works fine.
NSString * Talkin1 = #"Hello I am an annoying string";
//NSString * Talkin2 = [Talkin1 substringToIndex:5];
[_window setMultiMessage:Talkin1];
So it works perfect (except for splitting the string like I want) when I use #" " but not when I use any result of substringToIndex or substringFromIndex.
I am new to objective c so I assume it is something basic I am missing. Any help would be wonderful! Thank you in advance.
(Assuming no GC.)
-substringToIndex: returns an autoreleased object. But you are not retaining the object in the setter, thus no one "owns" the Talkin2 and it will be deallocated "later". You need to copy or retain the string in the setter.
-(void) setMultiMessage:(NSString*)messageTwo {
if (multiMessage != messageTwo) {
[multiMessage release];
multiMessage = [messageTwo retain];
}
}
Actually you should really use Objective-C 2.0's declared property feature. It allows correct setter and getter be generated automatically.
#interface .... { ... }
...
#property(copy) NSString* multiMessage; // <--
...
#end
#implementation ....
#synthesize multiMessage; // <--
...
#end
...
_window.multiMessage = Talkin2;
// or: [_window setMultiMessage:Talkin2];
NSString* talking = _window.multiMessage;
// or: talking = [_window multiMessage];

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