How to dynamically build up the arguments for NSLog? - iphone

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];

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

how to sort an array of dictionaries depending on several properties of the dictionary

I need to sort an array of dictionaries (as close to quick sort as possible), using a keyed object from the dictionary, However because of the complexity of my dictionary (number of keyed valued) and data that can be returned in them I need to be able to sort each dictionary based off several of the keyed values in the dictionary.
for instance, say if dictionay keyvalue1 in array[0], array[1] are equal, then check if keyvalue2 is equal, if yes then continue down the list of keyvalues till the two items that you can sort array[0] & [1] on.
I have read about NSSortDescriptor and specifying the property to compare with where you can set ascending or descending, but I am not sure if this could be used to achive what I am wanting to achieve.
I have been given an example of what I need to do but its in delphi, which I am not very familiar with, however I think the example gives some insight as to what I am trying to do.
//
Result := AnsiCompareText (left.property1, right.Property1);
if Result <> 0 then Exit;
Result := AnsiCompareText (left.property2, right.Property2);
if Result <> 0 then Exit;
Result := AnsiCompareText (left.property3, right.Property3);
if Result <> 0 then Exit;
Result := AnsiCompareText (left.property4, right.Property4);
if Result <> 0 then Exit;
Result := AnsiCompareText (left.property5, right.Property5);
if Result <> 0 then Exit;
//
Hopefully this will give you some idea as to what I am trying to achieve, any help would be greatly appreciated been stuck on this for a while now, if you know of a similar solution in objective C i would love to hear it! :P
Lets assume you have a mutable array. Then you use this:
[myArray sortUsingComparator:^ NSComparisonResult(NSDictionary *d1, NSDictionary *d2)
{
// you have two items - use whatever complex logic you want, then return
// one of NSOrderedAscending, NSOrderedSame, NSOrderedDescending
} ];
For instance, suppose all you cared about was a "name" property:
[myArray sortUsingComparator:^ NSComparisonResult(NSDictionary *d1, NSDictionary *d2)
{
NSString *n1 = [d1 objectForKey:#"name"];
NSString *n2 = [d2 objectForKey:#"name"];
return [n1 localizedCompare:n2];
} ];
There really nice thing about this technique is that the logic can be arbitrarily complex.

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 .

NS String comparison fails with stringWithFormat

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