How to get rid of all this garbage in NSLog? - iphone

When I use
NSLog(#"fooBar")
it prints out a lot of stuff I don't want:
2009-09-03 13:46:34.531 MyApp[3703:20b] fooBar
Is there a way to print something to the console without this big prefix?
I want to draw a table and some other things in the console so that space is crucial...

This is from Mark Dalrymple at borkware.com
http://borkware.com/quickies/single?id=261
A Quieter NSLog (General->Hacks) [permalink]
// NSLog() writes out entirely too much stuff. Most of the time I'm
// not interested in the program name, process ID, and current time
// down to the subsecond level.
// This takes an NSString with printf-style format, and outputs it.
// regular old printf can't be used instead because it doesn't
// support the '%#' format option.
void QuietLog (NSString *format, ...)
{
va_list argList;
va_start (argList, format);
NSString *message = [[[NSString alloc] initWithFormat: format
arguments: argList] autorelease];
printf ("%s", [message UTF8String]);
va_end (argList);
} // QuietLog

This is a variation on the borkware quickies: http://cocoaheads.byu.edu/wiki/a-different-nslog . It prints the file and line number of where the log takes place. I use it all the time.

NSLog just prints to strerr. Use fprintf instead.
fprintf(stderr, "foobar");

Related

NSMutableArray Not showing actual values when NSLog in iphone application

I am doing an NSLog of an array but instead of data it shows the following values. I do not know how to fix this issue and get the values from the array
if(!surveyQuestions){
surveyQuestions=[[NSMutableArray alloc]init];
}
Total Survey Questions 3
2012-07-31 08:54:53.555 SQL[442:9203] SurveyQuestions (
"<QuestionData: 0x4da10f0>",
"<QuestionData: 0x4b9f120>",
"<QuestionData: 0x4ba42e0>"
)
I'm not sure what you're trying to do but: it's certain that the poor array object has no idea what and how your own custom class does, its best possibility to print an instance of your class is to call its description method, which you see, and which is not really helpful. You maybe want to do two things:
I. If you only want to print your objects like this, override the description method of your class and use some format string (given that you haven't written a single line of code I have to fall back to guess):
- (NSString *)description
{
return [NSString stringWithFormat:#"Name: %#, address: %#", self.name, self.address];
}
II. If you want to use the data of your class elsewhere, you probably want to loop through its properties manually:
for (QuestionData *d in surveyQuestions)
{
NSLog(#"%#", d.name);
// etc.
}
You need to do something like this:
NSArray *theArray = [[NSArray alloc] initWith...];
NSLog(#"array contents: %#", theArray);

Write stderr on iPhone to both file and console

I'm following the suggestion in the answer here for redirecting NSLog output on an iOS device to a file, which works great. The problem is that it no longer shows up in the console on the device. What I'd really like is a way to tee the stderr stream to both the console and the file. Does anyone have an idea how to do that?
I found an acceptable answer on another thread (NSLog() to both console and file).
The solution provided there is to only redirect to a file if a debugger is not detected, like this:
if (!isatty(STDERR_FILENO))
{
// Redirection code
}
Thanks to Sailesh for that answer.
Once you freopen() the file descriptor, you can read from it and do as you please with the data. Some ideas from this will be useful to you.
You could either write it back out to stdout, or try to write directly to /dev/console. I've never tried to open /dev/console on an iPhone, but I'm guessing it's possible despite being outside of the sandbox. I'm not sure how the app review process will treat it.
Or you can redirect to a TCP socket and view on a remote telnet client. No need for XCode this way!
Basically:
Create a standard C function which calls an Obj-C static method:
void tcpLogg_log(NSString* fmt, ...)
{
va_list args;
va_start(args, fmt);
[TCPLogger tcpLog:fmt :args];
va_end(args);
}
The static Obj-C method:
(void)tcpLog:(NSString*)fmt :(va_list)args
{
NSLogv(fmt, args);
if(sharedSingleton != nil && sharedSingleton.socket != nil)
{
NSString *time = [sharedSingleton.dateFormat stringFromDate:[NSDate date]];
NSString *msg = [[NSString alloc] initWithFormat:fmt arguments:args];
mach_port_t tid = pthread_mach_thread_np(pthread_self());
NSString *str = [NSString stringWithFormat:#"%#[%X]: %#\r\n", time, tid, msg];
NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];
[sharedSingleton.socket writeData:data
withTimeout:NETWORK_CLIENT_TIMEOUT_PERIOD
tag:0];
}
}
Then in your .pch file, add the following lines to override NSLog()
define NSLog(...) tcpLogg_log(__VA_ARGS__);
void tcpLogg_log(NSString* fmt, ...);
Of course more details are required to handle the TCP Socket. Working source code is available here:
https://github.com/driedler/iOS-TCP-Logger/wiki/About

String manipulation in objective-c

this is hard to describe but I am currently catching a string from a database, this string can be 1-4 characters long, however I am wanting to always display 4 characters, so if i get say a string back that is 34, i want it to be 0034.
I have set up a method to catch the string so now I just need to figure out how to do this. what I then plan to do is feed that string into a NSArray so I can send each [i'th] of the array off to 4 differetn methods that control animations in my app.
The reason its in string format is because I have had to bounce it round from hex, to int to string for various formatting reasons within the application.
this is my code i have so far. Suggestions/solutions would be great thankyou, I am so new its hard to find solutions for stuff like string manipulation etc..
//... other method I am getting the string from/.
[self formatMyNumber:dataString];
///..
-(void)formatMyNumber:(NSString *)numberString{
//resultLabel.text = numberString; //check to make sure string makes it to here.
//NSLog(#"hello From formatMyNumber method"); //check
}
//..
//the with send off each character to 4 animation methods that accept integers.
- (void)playAnimationToNumber:(int)number{
//...
//UpDated... weird stuff happening.
here is my method so far.
//Number Formatter
-(void)formatMyNumber:(NSString *)numberString{
NSLog(#"This is what is passed into the method%#",numberString);
int tempInt = (int)numberString;
NSLog(#"This is after I cast the string to an int %i",tempInt);
//[NSString alloc] stringWithFormat:#"%04d", numberString];
NSString *tempString = [[NSString alloc] initWithFormat:#"%04d", tempInt];
NSLog(#"This is after I try to put zeros infront %#",tempString);
//resultLabel.text = tempString;
//NSLog(#"hello From formatMyNumber method");
}
this is the output.
[Session started at 2011-06-19
16:18:45 +1200.] 2011-06-19
16:18:54.615 nissanCode0.1[4298:207]
731 2011-06-19 16:18:54.616
nissanCode0.1[4298:207] 79043536
2011-06-19 16:18:54.617
nissanCode0.1[4298:207] 79043536
2011-06-19 16:18:54.617
nissanCode0.1[4298:207] hello From
formatMyNumber method
As far as the number of zeros preceding your string goes there are a couple of ways to do this. I'd suggest:
NSString *myString = [NSString stringWithFormat:#"%04d",[dataString intValue]];
Is it possible you could have the number in integer form instead of string form? If so, it's pretty easy to use [NSString stringWithFormat:#"%04d", number]. See here for a list of the possible format specifiers.
See what stringWithFormat: can do. I realize you mentioned your numbers are NSStrings, but if they were ints, or you convert them back to ints, the following may do the trick. Modify the following to best suit your need:
return [NSString stringWithFormat:#"%04d", number];

Using va_list and getting EXC_BAD_ACCESS

Similar to how NSLog takes variable argument list, I want to create my own method.
I have my method declared like this but when I try to access the 'args' variable, I get an EXEC_BAD_ACCESS. What is that I'm not doing correctly here?
- (void)info:(NSString *)formatString, ...
{
va_list args;
va_start(args, formatString);
NSLog(#"formatString value: %#", formatString);
// The following line causes the EXEC_BAD_ACCESS
NSLog(#"args value: %#", args);
// This is what I'm trying to do:
NSLog(formatString, args);
va_end(args);
}
I was following the 'va_list in Cocoa' section from this blog:
http://cocoawithlove.com/2009/05/variable-argument-lists-in-cocoa.html
There are a couple of mistakes in your code. Firstly, args cannot be printed directly as is. It is a reference to several arguments, and trying to print it using NSLog("%#") will not work. What you can do is use NSLogv() to print it (e.g. NSLogv(format, args);) instead.
Or you can do what I do, and use this function:
void print (NSString *format, ...) {
va_list args;
va_start(args, format);
fputs([[[[NSString alloc] initWithFormat:format arguments:args] autorelease] UTF8String], stdout);
va_end(args);
}
The "%#" format directive takes an argument, interprets it as an Objective-C object and sends it the "description" selector. That needs to return a NSString which is printed.
So you code is trying to execute ‘[args description]‘, but args is not a Objective-C object, it's of type ‘va_list‘. Hence the exception.
See your link, the implementation of "setContentByAppendingStrings:" shows how do get the arguments out of your va_list.
Morning lads,
I just have been confronted with the very similar issue. Here is what I was doing:
+ (void) l:(D3LogLevel)p_logLevel s:(NSString *)p_format, ...
{
if (p_logLevel >= logLevel) {
va_list v_args;
va_start(v_args, p_format);
NSLog(#"[%d] %#", p_logLevel, [NSString stringWithFormat:p_format, v_args]);
va_end(v_args);
}
}
Which, as DarkDust here has described accurately, uses v_args as an Objective-C object when it is not. Here is the culprit call:
[NSString stringWithFormat:p_format, v_args]
Hence the modification to take a va_list:
[[NSString alloc] initWithFormat:p_format arguments:v_args]
Making use of the appropriate method initWithFormat which signature is:
- (id)initWithFormat:(NSString *)format arguments:(va_list)argList
We can see that the type is right and everything becomes crystal clear. The full rewritten method is then:
+ (void) l:(D3LogLevel)p_logLevel s:(NSString *)p_format, ...
{
if (p_logLevel >= logLevel) {
va_list v_args;
va_start(v_args, p_format);
NSLog(#"[%d] %#", p_logLevel, [[NSString alloc] initWithFormat:p_format arguments:v_args]);
va_end(v_args);
}
}
It works like a charm!

NSString stringWithFormat swizzled to allow missing format numbered args

Based on this SO question asked a few hours ago, I have decided to implement a swizzled method that will allow me to take a formatted NSString as the format arg into stringWithFormat, and have it not break when omitting one of the numbered arg references (%1$#, %2$#)
I have it working, but this is the first copy, and seeing as this method is going to be potentially called hundreds of thousands of times per app run, I need to bounce this off of some experts to see if this method has any red flags, major performance hits, or optimizations
#define NUMARGS(...) (sizeof((int[]){__VA_ARGS__})/sizeof(int))
#implementation NSString (UAFormatOmissions)
+ (id)uaStringWithFormat:(NSString *)format, ... {
if (format != nil) {
va_list args;
va_start(args, format);
// $# is an ordered variable (%1$#, %2$#...)
if ([format rangeOfString:#"$#"].location == NSNotFound) {
//call apples method
NSString *s = [[[NSString alloc] initWithFormat:format arguments:args] autorelease];
va_end(args);
return s;
}
NSMutableArray *newArgs = [NSMutableArray arrayWithCapacity:NUMARGS(args)];
id arg = nil;
int i = 1;
while (arg = va_arg(args, id)) {
NSString *f = [NSString stringWithFormat:#"%%%d\$\#", i];
i++;
if ([format rangeOfString:f].location == NSNotFound) continue;
else [newArgs addObject:arg];
}
va_end(args);
char *newArgList = (char *)malloc(sizeof(id) * [newArgs count]);
[newArgs getObjects:(id *)newArgList];
NSString* result = [[[NSString alloc] initWithFormat:format arguments:newArgList] autorelease];
free(newArgList);
return result;
}
return nil;
}
The basic algorithm is:
search the format string for the %1$#, %2$# variables by searching for %#
if not found, call the normal stringWithFormat and return
else, loop over the args
if the format has a position variable (%i$#) for position i, add the arg to the new arg array
else, don't add the arg
take the new arg array, convert it back into a va_list, and call initWithFormat:arguments: to get the correct string.
The idea is that I would run all [NSString stringWithFormat:] calls through this method instead.
This might seem unnecessary to many, but click on to the referenced SO question (first line) to see examples of why I need to do this.
Ideas? Thoughts? Better implementations? Better Solutions?
Whoa there!
Instead of screwing with a core method that you very probably will introduce subtle bugs into, instead just turn on "Static Analyzer" in your project options, and it will run every build - if you get the arguments wrong it will issue a compiler warning for you.
I appreciate your desire to make the application more robust but I think it very likely that re-writing this method will more likely break your application than save it.
How about defining your own interim method instead of using format specifiers and stringWithFormat:? For example, you could define your own method replaceIndexPoints: to look for ($1) instead of %1$#. You would then format your string and insert translated replacements independently. This method could also take an array of strings, with NSNull or empty strings at the indexes that don't exist in the “untranslated” string.
Your method could look like this (if it were a category method for NSMutableString):
- (void) replaceIndexPointsWithStrings:(NSArray *) replacements
{
// 1. look for largest index in "self".
// 2. loop from the beginning to the largest index, replacing each
// index with corresponding string from replacements array.
}
Here's a few issues that I see with your current implementation (at a glance):
The __VA_ARGS__ thingy explained in the comments.
When you use while (arg = va_arg(args, id)), you are assuming that the arguments are nil terminated (such as for arrayWithObjects:), but with stringWithFormat: this is not a requirement.
I don't think you're required to escape the $ and # in your string format in your arg-loop.
I'm not sure this would work well if uaStringWithFormat: was passed something larger than a pointer (i.e. long long if pointers are 32-bit). This may only be an issue if your translations also require inserting unlocalised numbers of long long magnitude.