Do you have any unique or special uses of NSLog you use for debugging?
I like to use this format for debugging.
NSLog( #"<%p %#:(%d)> %#", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
Of course, you'll want to wrap this in your own method or function for ease of use. I use the preprocessor and also only enable it for my own use and special builds I send out to beta testers. This is copied from my answer to this question, by the way.
#define DEBUG_MODE
#ifdef DEBUG_MODE
#define DebugLog( s, ... ) NSLog( #"<%p %#:(%d)> %#", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#else
#define DebugLog( s, ... )
#endif
This makes DebugLog act just like NSLog, but displays the filename and line-number where it was called:
2009-05-23 17:23:40.920 myproject[92523:10b] <AppCon.m:(8)> My debug message...
I use some macros with NSLog to quickly debug the contents of cocoa's NSPoint, NSSize, and NSRect structs:
#define LogPoint(POINT) CMLog(#"%s: (%0.0f, %0.0f)",\
#POINT, POINT.x, POINT.y)
#define LogSize(SIZE) CMLog(#"%s: %0.0f x %0.0f",\
#SIZE, SIZE.width, SIZE.height)
#define LogRect(RECT) CMLog(#"%s: (%0.0f, %0.0f) %0.0f x %0.0f",\
#RECT, RECT.origin.x, RECT.origin.y,\
RECT.size.width, RECT.size.height)
These can be used as follows:
LogPoint(somePoint);
LogSize(someSize);
LogRect(someRect);
And they produce the following output:
somePoint: (100, 200)
someSize: 12 x 440
someRect: (120, 240) 326 x 74
NSLog(#"%s", __func__);
prints the current method signature or function name.
This is not an NSLog feature, however it’s very handy for use with NSLog: you can use %# placeholder for objects, and their descriptions will be displayed:
NSLog (#"The object is %#", someKindOfObjectWhichYouWantToDisplay);
This way you can quikly see what object you get returned, for example. This works by sending “description” selector an object, which can be, of course, implemented in your own objects.
Here's one that lets you indent some portions of your debugging logs to make things more readable:
// MyDebugStuff.h:
void MyLog_Indent();
void MyLog_Outdent();
void MyLog(NSString * format, ...);
// MyDebugStuff.m:
int logIndentLevel = 0;
void MyLog_Indent() { logIndentLevel++; }
void MyLog_Outdent() { if (logIndentLevel > 0) { logIndentLevel--; } }
void MyLog(NSString * format, ...)
{
va_list args;
va_start(args, format);
NSString * indentString = [[NSString stringWithString:#""]
stringByPaddingToLength:(2*LogIndentLevel)
withString:#" "
startingAtIndex:0];
NSLogv([NSString stringWithFormat:#"%#%#", indentString, format], args);
va_end(args);
}
This can be used like so:
MyLog(#"Hello, world");
MyLog_Indent();
MyLog(#"Step 1");
MyLog(#"Step 2");
MyLog_Indent();
MyLog(#"Step 2a");
MyLog(#"Step 2b");
MyLog_Outdent();
MyLog(#"Step 3");
MyLog_Outdent();
MyLog(#"Goodbye, cruel world!");
And will produce:
Hello, world
Step 1
Step 2
Step 2a
Step 2b
Step 3
Goodbye, cruel world!
Related
Maybe this will be obviously simple for most of you, but could you please give an example how to create similar methods (in Objective-C) and functions in C to create functions like NSString's stringWithFormat:, or NSLog().
Just to remind:
[NSString stringWithFormat:#"example tekst %i %# %.2f", 122, #"sth", 3.1415"];
NSLog(#"account ID %i email %#", accountID, email);
I'd like to create the similar to NSString's method stringWithFormat:, NSURL - urlWithFormat.
What these are called, generally, is "variadic functions" (or methods, as it were).
To create this, simply end your method declartion with , ..., as in
- (void)logMessage:(NSString *)message, ...;
At this point you probably want to wrap it in a printf-like function, as implementing one of those from scratch is trying, at best.
- (void)logMessage:(NSString *)format, ... {
va_list args;
va_start(args, format);
NSLogv(format, args);
va_end(args);
}
Note the use of NSLogv and not NSLog; consider NSLog(NSString *, ...); vs NSLogv(NSString *, va_list);, or if you want a string; initWithFormat:arguments: on NSString *.
If, on the other hand, you are not working with strings, but rather something like
+ (NSArray *)arrayWithObjects:(id)object, ... NS_REQUIRES_NIL_TERMINATION;
things get a lot easier.
In that case, instead of a vprintf-style function, use a loop going through args, assuming id as you go, and parse them as you would in any loop.
- (void)logMessage:(NSString *)format, ... {
va_list args;
va_start(args, format);
id arg = nil;
while ((arg = va_arg(args,id))) {
/// Do your thing with arg here
}
va_end(args);
}
This last sample, of course, assumes that the va_args list is nil-terminated.
Note: In order to make this work you might have to include <stdarg.h>; but if memory serves, this gets included in connection with NSLogv, meaning it comes down by way of "Foundation.h", therefore also "AppKit.h" and "Cocoa.h", as well as a number of others; so this should work out of the box.
- (void)methodWithFormat:(NSString*)format, ... {
va_list args;
va_start(args,format);
//loop, get every next arg by calling va_arg(args,<type>)
// e.g. NSString *arg=va_arg(args,NSString*) or int arg=(args,int)
va_end(args);
}
If you want to pass the variable arguments to stringWithFormat:, use something like:
NSString *s=[[[NSString alloc] initWithFormat:format arguments:args] autorelease];
One thing to mention here is that, the first NSString parameter here comes as format, and the other are passed in the variable argument. right? So before entering the for loop, you have one parameter to handle.
- (NSString *) append:(NSString *)list, ...
{
NSMutableString * res = [NSMutableString string];
[res appendString:list];
va_list args;
va_start(args, list);
id arg = nil;
while(( arg = va_arg(args, id))){
[res appendString:arg];
}
va_end(args);
return res;
}
- (void) test_va_arg
{
NSString * t = [self append:#"a", #"b", #"c", nil];
STAssertEqualObjects(#"abc", t, #"");
}
I have Logger.h with following code:
#import <Foundation/Foundation.h>
#import <asl.h>
#define LogDebug(format, ...){ \
Logger(ASL_LEVEL_DEBUG, format,##__VA_ARGS__); }
#define Logger(LEVEL,format, ...) \
LogLocal(LEVEL,format,##__VA_ARGS__);
#define LogLocal(LEVEL, format, ...) \
va_list arg_list; \
va_start(arg_list, format); \
// ...\
va_end(arg_list); \
I call log from Obj-C as:
LogDebug(#"Name is called with flag: %#", collectName ? #"YES" : #"NO");
However I get an error:
'va_start' used in function with fixed args
How to get rid of this problem?
EDIT 1:
I tried to call also: AFLogLocal(LEVEL,format,...); - same error
EDIT 2:
if I'll remove asl_log and replace with NSLog - it will work:
#define LogLocal(LEVEL,format, ...) \
NSLog((#"XXX: %s " format), __PRETTY_FUNCTION__, ##__VA_ARGS__);
EDIT 3
credits to #Amin Negm-Awad, replaced LogLocal with function in .m file as:
void LogLocal(int level, NSString *format, ...){
va_list arg_list;
va_start(arg_list, format);
va_end(arg_list);
NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list];
asl_add_log_file(NULL, STDERR_FILENO);
asl_log(NULL, NULL, (level), "XXXX: %s", [formattedString UTF8String]);
}
works as expected.
You use macros that are expanded. But they do not create a scope or a call on expansion. It's simple text replacement.
// Somewhere in a function
void f(void)
{
…
LogDebug(#"Name is called with flag: %#", collectName ? #"YES" : #"NO");
…
}
This will expand at the end to something like this:
// Somewhere in a function
void f(void)
{
…
// LogDebug(#"Name is called with flag: %#", collectName ? #"YES" : #"NO");
va_list arg_list;
va_start(arg_list, #"Name is called with flag: %#");
// ...
va_end(arg_list);
…
}
Since f() takes no vargs, there are no vargs.
It might help to make LogLocal() a function, not a macro:
void LogLocal( int level, NSString *format, ... );
Implementation:
void LogLocal( int level, NSString *format, ... )
{
va_list arg_list;
va_start(arg_list, format);
// ...
va_end(arg_list);
}
To your Edit 2:
Yes, this works, because you do not touch the arg list, but pass the args to a function taking vargs. But you do not need that trick. Instead make LogLocal() a function and it will be called similar to NSLog() without any error.
I can't figure out why I get
use of undeclared identifier _cmd did you mean rcmd
on the line where NSAssert is.
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int x = 10;
NSAssert(x > 11, #"x should be greater than %d", x);
[pool drain];
return 0;
}
Inside every Objective-c method there are two hidden variables id self and SEL _cmd
so
- (void)foo:(id)bar;
is really
void foo(id self, SEL _cmd, id bar) { ... }
and when you call
[someObject foo:#"hello world"]
it is actually
foo( someObject, #selector(foo), #"hello world")
If you cmd-click on NSAssert to jump to it's definition you will see that it is a macro that uses the hidden _cmd variable of the method you are calling it from. This means that if you are not inside an Objective-c method (perhaps you are in 'main'), therefore you don't have a _cmd argument, you cannot use NSAssert.
Instead you can use the alternative NSCAssert.
NSAssert is only meant to be used within Objective-C methods. Since main is a C function, use NSCAssert instead.
Try to replace
NSAssert(x > 11, [NSString stringWithFormat:#"x should be greater than %d", x]);
with
NSCAssert(x > 11, [NSString stringWithFormat:#"x should be greater than %d", x]);
You have to wrap your string in a NSString class if you want to use format parameters. That is because #"" is a default constructor for a plain NSString. The way it is written now gives a third parameter to the NSAssert function and messes with it.
NSAssert(x > 11, [NSString stringWithFormat:#"x should be greater than %d", x]);
TL;DR - stick with stray NSAssert() - don't try this in production
Original code
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int x = 10;
NSAssert(x > 11, #"x should be greater than %d", x);
[pool drain];
return 0;
}
Build failure
Compiling file hello.m ...
hello.m:9:5: error: use of undeclared identifier '_cmd'
NSAssert(x > 11, #"x should be greater than %d", x);
^
/usr/include/Foundation/NSException.h:450:32: note: expanded from macro 'NSAssert'
handleFailureInMethod: _cmd \
^
hello.m:9:5: error: use of undeclared identifier 'self'
/usr/include/Foundation/NSException.h:451:17: note: expanded from macro 'NSAssert'
object: self \
^
2 errors generated.
Based on explanation by #hooleyhoop #Robert and
id
self
SEL,
the following dirty hack may be applicable if I insist on using
NSAssert() instead of
NSCAssert()
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int x = 10;
// Dirty hack
SEL _cmd=NULL;
NSObject *self=NULL;
NSAssert(x > 11, #"x should be greater than %d", x);
[pool drain];
return 0;
}
Build & run
Compiling file hello.m ...
Linking tool hello ...
2021-03-04 21:25:58.035 hello[39049:39049] hello.m:13 Assertion failed in (null)(instance), method (null). x should be greater than 10
./obj/hello: Uncaught exception NSInternalInconsistencyException, reason: hello.m:13 Assertion failed in (null)(instance), method (null). x should be greater than 10
Hooray it works! But, alas, please stay away from it :)
Is there any function that does what NSLog does but without the new line at the end?
see this http://borkware.com/quickies/one?topic=NSString
excerpted from that page:
void LogIt (NSString *format, ...)
{
va_list args;
va_start (args, format);
NSString *string;
string = [[NSString alloc] initWithFormat: format arguments: args];
va_end (args);
printf ("%s\n", [string UTF8String]);
[string release];
} // LogIt
just customize the printf to suit your needs
You can use printf(), but the time won't be displayed, and you won't be able to use the "%#" sequence for objects.
That said, you can implement your own logging function, using printf(), and adding support for objects. You will need to know how to deal with C variable arguments.
you can use some trick. use special unicode character like:u00a0
NSLog(#"\n\n\n\u00a0")
I can't get my head around the syntax for multiple arguments in Objective-C. I have seen this question, but the answer hasn't helped me (yet).
Here is my code (actually I will want to eventually pass to NSString stringWithFormat, but getting an NSLog to work would be good enough for now):
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
[self log:#"blah blah %d", 32];
}
- (void)log:(NSString *)text, ... {
va_list args;
va_start(args, text);
NSLog(text, args);
}
The argument (or some argument) comes through, but it's got some weird value (output is blah blah 1606412704). How should I pass the values that come in via ...?
There's a variant of NSLog that accepts a va_list called NSLogv:
- (void) log:(NSString *)text, ... {
va_list args;
va_start(args, text);
NSLogv(text, args);
va_end(args);
}
The only way to forward the actual ... (not the va_list) is to use a macro. For example:
#define MyLog(f, ...) { \
NSLog(f, ##__VA_ARGS__); \
[someObject doSomething:f, ##__VA_ARGS__]; \
}
However, this should be used very sparingly, since macros can make code really obfuscated.
You could use -[NSString initWithFormat:arguments:]:
- (void)log:(NSString *)text, ...
{
va_list args;
va_start(args, text);
NSString *log_msg = [[[NSString alloc] initWithFormat:text arguments:args] autorelease];
NSLog(#"%#", log_msg);
}