NS String comparison fails with stringWithFormat - iphone

I have two NSStrings with the same value
this failed for me:
if (button.controlName == controlName) {
return button;
}
this worked:
if ([button.controlName compare: controlName] == NSOrderedSame) {
return button;
}
Is this just how strings are compared in objective c? Or should the first statement have worked as well? Why might the first statement have failed? I know it worked for other strings.
The strings it does not work for are initialized like this:
button.controlName = [NSString stringWithFormat:#"controlName%d", i]

With NSString you should use isEqualToString: instead of compare.
[button.controlName isEqualToString:controlName]
Read more the why (and also why it worked for some other strings)
Objective-C is a fairly thin layer on top of standard C. As a result obj-c, just as in normal c, doesn't have operator overloading.
NSString *controlName = #"bobDole";
The above code creates a pointer to the string #"bobDole", controlName is not the value itself, but instead is really just a long integer that says the memory address of an object.
When using pointers and comparing them using the == operator like (mutableCopy is being used to prevent the compiler from optimizing out the validity of this example.)
NSString *string1 = #"bobDole";
NSString *string2 = [string1 mutableCopy];
NSLog(#"%d", string1 == string2);
The above code will always print false (or zero in this case), even though both objects are NSStrings, and both contain the value of #"bobDole". This is because the value of string1 is actually a hex number like 0x0123456 and string2 could be something like 0x0987654. So really the above comparison looks like this to the computer:
NSLog(#"%d", 0x0123456 == 0x0987654);
So when comparing strings (or any other object), always use one of the isEqual methods, never use the == operator.
Now as to why it worked for some other strings:
As mentioned above when using the == operator you're actually doing pointer comparison. You'll also notice in my above example I used mutableCopy instead of the following:
NSString *string1 = #"bobDole";
NSString *string2 = #"bobDole";
The reason I did such was that the compiler will look at those two statements know they share the same immutable value and optimize them so they point at the same value in memory. Thus making the pointer values of the two identical.
The compiler also makes the same optimizations for these methods of string initialization.
NSString *string3 = [NSString stringWithString:#"bobDole"];
NSString *string4 = [NSString stringWithString:string1];
NSString *string5 = [string1 copy];
Because of this optimization by the compiler and runtime all 5 pointers point to the same memory location and are thus equal to each other when compared via ==.
This may be kinda long, but I tried to make it accessible and understandable. Hope it helps.

1st statement just compares pointers, but not string values, so to compare strings you should use -isEqualToString as Bryan points.

There's a method called isEqualToString to compare two NSStrings.
if([button.controlName isEqualToString:controlName])
...

Related

What type of arguments does [NSString stringWithFormat] take?

I know that the method stringWithFormat is usually used as follows:
[NSString stringWithFormat: #"%# %#", arg1, arg2];
My question is, what exactly is the type of the last argument of this method (the one with ... in the specs).
I want to be able to do something like the following:
NSArray *array = [NSArray arrayWithObjects: #"Hi", #"Bob"];
[NSString stringWithFormat: #"%# %#", array]
As you can see one datastructure array is holding all of the arguments. Unfortunately, this doesn't work. Is there a way to do this so that I don't have to explicitly list out all of my arguments in the stringWithFormat method?
What type of arguments does [NSString stringWithFormat] take?
Whatever type you tell it to do so using its format string.
My question is, what exactly is the type of the last argument of this method
That's not the last argument. This is a concept called a "variadic function" (or method, in this case). The ... indicates that the function can take any number of arguments. This is a feature of C, and you can use it by using the types, macros and functions from the C standard library header <stdarg.h>. Example: this creates an array with the objects given.
- (NSArray *)arrayWithCountAndObjects:(int)count, ...
{
NSMutableArray *arr = [NSMutableArray array];
va_list args;
va_start(args, count);
for (int i = 0; i < count; i++) {
[arr addObject:va_arg(args, id)];
}
va_end(args);
return arr;
}
However, what you should do here is simply joining the items in the array instead of hacking with variadic arguments, because that's unnecessary:
NSArray *arr = #[#"Hello", #" ", #"world!"];
NSString *s = [arr componentsJoinedByString:#""];
You don't want to use string with format, if the array holds strings, then you want to use the array method, componentsJoinedByString:. Just pass #" " as the string if you want a space between the strings.
It takes variable arguments. If you want to look up the type of function, it's called "variadic".
In this specific case, those arguments are determined by the formatting string you specify. %# means any cocoa object (inheriting from NSObject). %s means a c string (null-terminated char array). %d means an int, %f means a float or double, and so on. There's quite a lot of them (all except %# are borrowed from C's printf function). You can find full details here: http://www.cplusplus.com/reference/cstdio/printf/ and here https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Strings/Articles/FormatStrings.html#//apple_ref/doc/uid/20000943
As for your attempt, that's not how it works. You'll just have to split the array up into separate parameters, or use componentsJoinedByString, or loop through manually if you need more control. But you could use a single %# to display the array in its own format, probably something like [First Value; Second Value].

Objective C Methods and syntax

I am a bit confused when working with objective-c. This part in particular confuses me a lot.
What is the purpose and/ or difference between writing code like this ...
object = [object method];
and
[object method];
Learning objective-c up until now, I always assumed that I could do something like this..
say I had already created this..
NSString *object = [[NSString alloc]initWithFormat:#"%#"];
then I could do what I want with that like so..
[object applyAnyMethodHere];
but now I'm seeing things like this ..
object = [object applyAnyMethodHere];
What is the difference between these two?
The first one (object = [object method];) is an assignment of whatever method returns.
The second one ([object method];) is just calling the method without paying attention to its return value (if any).
The third (NSString *object = [[NSString alloc]initWithFormat:#"#"]) declares variable and assigns the return value of the initWithFormat method called on the return value of the alloc class method.
In many programming languages, object = [object method]; is what is called an "assignment" statement. Consider:
x = y + z;
The statement is read by the computer from right to left to mean:
Calculate the sum of the variables y and z and then store it in the variable called x.
The contents of the right side of your expression don't matter so much as what is actually happening in the whole statement. In your example, the computer will:
Tell an object named "object" to perform "method" and store the results back in "object".
However, you don't always want to store the results of a method call. For example, if you want to present an alert view, you may simply call:
[myalertView show];
Notice that there is no assignment happening. Assignment is not required, unless you want to store the value returned by a method call.
Also, consider this:
NSInteger x = 5;
There is no method call, but there is an assignment. Your example of object = [object method]; is simply a more complex version of that.

Why does declaring pointer makes it contents nil? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Declared but unset variable evaluates as true?
Why does this code work on Mac OS
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString *str;
NSLog(#"%#", [str length]);
NSLog(#"Hello, World!");
[pool drain];
return 0;
}
and console output is
2011-09-23 15:37:17.481 Untitled1[80021:903] (null)
2011-09-23 15:37:17.495 Untitled1[80021:903] Hello, World!
but crashes on iPhone ?
Two mistakes.
First, you declare pointer str, don't set it's value and use it in NSLog.
Second, you use formatting %#, but supply integer value ([str length]), result is undefined. That's why on simulator it prints null, but crashes on device.
You were just lucky. Local variables are uninitialised and could contain anything. It's likely that on OS X, str contains 0 (aka nil), sending -length to nil returns 0. Then you are treating the length (which is an NSInteger) as a pointer for the NSLog.
If the length is 0, the NSLog will treat it as a nil pointer if the format specifier is %# and will print (null).
If str has a random non zero value, either your program will crash when you send length or if it miraculously works and returns a non zero length, it'll probably crash trying to treat it as an object pointer in NSLog
the value is never initialized (or set). it could be any random value. in debug, it may be zero-iniitalized.
a crash (or some UB) is what you should expect in this case. so... turn up your compiler and static analyzer warnings, fix the issues, and always initialize your values.
try this.
NSString *str=#"";
NSLog(#"%d", [str length]);
See ,
You can call only release method over a nil object. other than release method we cannot call any method over a nil.
Here you are calling a method ( length )over an object which is pointing to nil , in this case it should point to nsstring object in general before sending length message .

How to dynamically build up the arguments for NSLog?

Example: I have a complex method that does a lot of stuff, and at the end I want to print a report with NSLog. NSLog wants a string, and then an arbitrary number of arguments. So lets say there are these possible values which can be logged:
A
B
C
D
E
F
It can happen that -for example- C and D are not logged, but the whole rest. How would I build up a dynamic thing which represent the value arguments for NSLog?
I choose NSLog for this question because it may be simpler aus NSPredicate and SUBQUERY. It seems impossible to build an NSPredicate format string dynamically while using an NSMutableString and appendFormat:... it results always in compile errors for the predicate. I guess that NSPredicate does something different with it's provided format values than NSMutableString -appendFormat does.
So if there was a way to feed NSPredicate with:
1) a huge, dynamically created format string
2) an huge, dynamically created "list" of arguments"
that would be cool.
Something like this should do it, conditionally append parts to the string:
NSMutableString* logMsg = [NSMutableString stringWithFormat:#"%# %# %#", A, B, C];
if (C) [logMsg appendFormat:#" %#", C];
if (D) [logMsg appendFormat:#" %#", D];
[logMsg appendFormat:#" %# %#", E, F];
NSLog(#"%#", logMsg);
Your underlying problem shouldn't be a problem. Just use +predicateWithFormat:argumentArray:. What issue are you having building this up?
If you're collecting a variable list of strings to be output at one time, simply use a NSMutableArray adding a line of log output as needed. Then at the end of the process, joing the components with a string:
NSMutableArray *logLines = [[NSMutable alloc] initWithCapacity:10];
...
NSLog(#"Multiple-line output:\n%#",[logLines componentsJoinedByString:#"\n"]);
[logLines release];

Objective-C syntax

I've been studying objective-c for a few days now. I keep coming across these two structures:
NSString * somestring
(NSString *) somestring
I understand the first simply sets a pointer to an NSString object, but what does the second construct do, and when should I use it?
What's the deal with the asterix marks?
Sorry if this question doesn't make any sense, I am completely new to this language, and haven't even reached the level of asking proper questions.
Main purpose -- I'm trying to decipher this method:
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger) row forComponent: (NSInteger)component
**Also, the classes I chose don't matter. **
What you want to understand is the method declaration syntax. In Objective-C, a method has a name which looks like this doSomethingWith:andAlso:inMode:. Each colon precedes an argument. When you declare or define a method, you also specify the types of the variables and their names; the type is given in parentheses. You also prepend things with a - for instance methods and a + for static methods, as well as the (parenthesized) return type. Now, you have
- (NSString*) pickerView:(UIPickerView*)pickerView
titleForRow:(NSInteger)row
forComponent:(NSInteger)component
{
...
}
When we decipher this, we find that:
-: It's an instance method.
(NSString*): The return type, NSString*.
pickerView:: The first part of the method name.
(UIPickerView*)pickerView: The first argument; its name is pickerView and it has type UIPickerView*.
titleForRow:: The second part of the method name.
(NSInteger)row: The second argument; its name is row and its type is NSInteger.
forComponent:: The third part of the method name.
(NSInteger)component: The third argument; its name is component and its type is NSInteger.
Thus, putting it all together, this defines the instance method pickerView:titleForRow:forComponent:; it returns an NSString*, and the three arguments it takes are of type UIPickerView*, NSInteger, and NSInteger, respectively. This method would be called as [obj pickerView:myPV titleForRow:myRow forComponent:myComponent].
And just for further reference: in isolation, if you have NSString* str, it declares a variable of type NSString*; and if you have (NSString*)obj, it (forcefully) converts that object to have type NSString*. This has no connection to the method declaration syntax.
Edit 1: I also saw that you were asking about the asterisks. In (Objective-)C, this represents a pointer. If you have an int x, say, then when you write int y = x and then y = 3, the original value of x is unchanged. If, however, you have an int* px, then you can write px = &x. (Note that ints, declared as int, are a completely different data type than int pointers declared as int*. Writing int y = &x is garbage, as is int* py = x, and so on.) This & is the "address of" operator; it finds where x is in memory and returns it. Now, if you write int* py = px and then py = &y, this won't change px. But if you write *px, you access the value currently stored in x, and you can change it: *px = 42 sets x to 42. For various reasons, when working with objects, people tend to like to work with references to them instead of their actual values; thus, in Objective-C, you only handle objects through pointers. What this means is that you will never see NSMutableArray x, only NSMutableArray* x; and that if you have NSMutableArray* y = x, then x and y are, roughly speaking, the same, and calling [x addObject:obj] affects y as well. There are more comprehensive tutorials out there—it's worth checking them out if you don't understand pointers—but this should suffice as an overview.
Edit 2: In another comment, you say you're coming from Ruby and Python. In Ruby (and I think Python, but I've used it less), every variable is a reference. This means that the basic use of pointers for object types should be familiar; as long as you never use & or *, they'll function in pretty much the same way. The difference between pointers and references is that you can take references to objects and create pointers to pointers. For instance, many methods end in ...error:(NSDictionary**)error. This effectively provides an extra return value; in the method, if something goes wrong, they can write *error = myErrorInfo. Since function arguments are copied, error = myErrorInfo wouldn't be visible; however, the pointer's referent is still the same, and so it can be assigned to. If you then write code such as:
NSDictionary* errorDict = nil;
[obj unsafeOperation:#"http://stackoverflow.com" error:&errorDict];
You pass in a pointer to errorDict so that the unsafeOperation:error: method can write to errorDict and you can see it.
mebFace - an asterisk represents a pointer. So whenever you see (eg) "NSString *" then it means you're dealing with a pointer to a NSString object. The method you're trying to decipher returns a pointer to a string as the result. It takes three parameters: a pointer to a UIPickerView, followed by a couple of integers. The reason that you don't use an asterisk in conjunction with the integers is because they are simple integers - not objects.
You should probably start off by reading a book on the C programming language. This is what Objective-C is built on top of.
Dave
The first one is variable declaration while second is type-casting. I.e. somestring is not of type NSString* so you type-cast it into being NSString*. I believe that the latter case is mostly (if not always) seen on the right side of assignment statement.
In ObjectiveC this comes from C and is also used in many other strictly-typed languages like Java, C# and so on.
In the updated question, your method signature defines that method returns pointer to NSString - (NSString *). And it accepts 3 parameters:
Pointer to UIPickerView object;
number of row;
number of column;
Confusingly, in Objective-c, both of your examples mean different things in different contexts.
When declaring a variable (and other places, but not everywhere else) the asterisk means 'pointer to', ie…
int foo; // An int variable
int *bar; // A pointer to an int
When it comes to objective-c objects we mostly deal with pointers to them, ie.. you will not see this..
NSString myString;
you will always see this
NSString *myString;
The reason why is quite involved, but is to do with where the memory for the object is allocated (the heap) and what happens when you pass a variable to a function or method. A value passed as an argument to a method is copied. It turns out that copying our string when we pass it to a method is probably not what we intended, so we pass a pointer to the string instead, and that is copied. Imagine a string that contains the complete works of Shakesphere, roughly 5 million bytes. It is inefficient to copy it every time we pass it to a method, but a pointer to it is only 4 bytes, and a pointer gives us just as good access to the data because it, well, points to it. This approach wouldn't get us anything for the int argument tho, as the int and the pointer to the int are going to be the same size (or ballpark, at least).
Your second example is a method or a method declaration. It might help to compare it to function definition in something like javascript:
function pickerViewTitleForRowForComponent( pickerView, row, component ) {
...do something here
return result;
}
Objective-c does things slightly better, sorry, i mean differently. The above would basically look like
- pickerView: pickerView titleForRow: row forComponent: component {
...do something here
return result;
}
The arguments are the same: pickerView, row and component. However, the above isn't a valid method because it is missing type info. ie. We must explicitly say what type each argument is and also the type of the object returned from the method.
- (return type)pickerView:(argument type)pickerView titleForRow:(argument type)row forComponent:(argument type)component {
...do something here
return result;
}
As you can probably see, in your example:
return type > a pointer to an NSString object -- (NSString *)
argument1 type > a pointer to a UIPickerView object -- (UIPickerView *)
argument2 type > an NSInteger -- (NSInteger)
argument3 type > an NSInteger -- (NSInteger)