Vincent Gable’s Blog

August 19, 2010

The Most Useful Objective-C Code I’ve Ever Written

Actually, it’s the most useful code I’ve extended; credit for the core idea goes to Dave Dribin with his Handy NSString Conversion Macro.

LOG_EXPR(x) is a macro that prints out x, no matter what type x is, without having to worry about format-strings (and related crashes from eg. printing a C-string the same way as an NSString). It works on Mac OS X and iOS. Here are some examples,

LOG_EXPR(self.window.screen);

self.window.screen = <UIScreen: 0x6d20780; bounds = {{0, 0}, {320, 480}}; mode = <UIScreenMode: 0x6d20c50; size = 320.000000 x 480.000000>>

LOG_EXPR(self.tabBarController.viewControllers);

self.tabBarController.viewControllers = (
“<UINavigationController: 0xcd02e00>”,
“<SavingsViewController: 0xcd05c40>”,
“<SettingsViewController: 0xcd05e90>”
)

Pretty straightforward, really. The biggest convenience so far is having the expression printed out, so you don’t have to write out a name redundantly in the format string (eg. NSLog(@"actionURL = %@", actionURL)). But LOG_EXPR really shows it’s worth when you start using scalar or struct expressions:

LOG_EXPR(self.window.windowLevel);

self.window.windowLevel = 0.000000

LOG_EXPR(self.window.frame.size);

self.window.frame.size = {320, 480}

Yes, there are expressions that won’t work, but they’re pretty rare for me. I use LOG_EXPR every day. Several times. It’s not quite as good as having a REPL for Cocoa, but it’s handy.

Give it a try.

How It Works

The problem is how to pick a function or format string to print x, based on the type of x. C++’s type-based dispatch would be a good fit here, but it’s verbose (a full function-definition per type) and I wanted to use pure Objective-C if possible. Fortunately, Objective-C has an @encode() compiler directive that returns a string describing any type it’s given. Unfortunately it works on types, not variables, but with C99 the typeof() compiler directive lets us get the type of any variable, which we can pass to @encode(). The final bit of compiler magic is using stringification (#) to print out the literal string inside LOG_EXPR()‘s parenthesis.

The Macro, Line By Line

1 #define LOG_EXPR(_X_) do{\
2 	__typeof__(_X_) _Y_ = (_X_);\
3 	const char * _TYPE_CODE_ = @encode(__typeof__(_X_));\
4 	NSString *_STR_ = VTPG_DDToStringFromTypeAndValue(_TYPE_CODE_, &_Y_);\
5 	if(_STR_)\
6 		NSLog(@"%s = %@", #_X_, _STR_);\
7 	else\
8 		NSLog(@"Unknown _TYPE_CODE_: %s for expression %s in function %s, file %s, line %d", _TYPE_CODE_, #_X_, __func__, __FILE__, __LINE__);\
9 }while(0)
  1. The first and last lines are a way to put {}‘s around the macro to prevent unintended effects. The do{}while(0); “loop” does nothing else.
  2. First evaluate the expression, _X_, given to LOG_EXPR once, and store the result in a _Y_. We need to use typeof() (which had to be written __typeof__() to appease some versions of GCC) to figure out the type of _Y_.
  3. _TYPE_CODE_ is c-string that describes the type of the expression we want to print out.
  4. Now we have enough information to call a function, VTPG_DDToStringFromTypeAndValue() to convert the expression’s value to a string. We pass it the _TYPE_CODE_ string, and the address of _Y_, which is a pointer, and has a known size. We can’t pass _Y_ directly, because depending on what _X_ is, it will have different types and could be of any size.
  5. VTPG_DDToStringFromTypeAndValue() returns nil if it can’t figure out how to convert a value to a string.
  6. Everything went well, print the stringified expression, #_X_, and the string representing it’s value, _STR_.
  7. otherwise…
  8. The expression had a type we can’t handle, print out a verbose diagnostic message.
  9. See line 1.

The VTPG_DDToStringFromTypeAndValue() Function

See the source in VTPG_Common.m:

It’s derived from Dave Dribin‘s function DDToStringFromTypeAndValue(), and is pretty straightforward: strcmp() the type-string, and if it matches a known type call a function, or use +[NSString stringWithFormat]:, to turn the value into a string.

The First Step Twords Fixing Your Macro Problem is Admitting it…

So yeah, maybe I went a little wild with macros here…

But it took out some WET-ness of the original code, and prevents me from accidentally mixing up types in a long wall of ifs, eg.

else if (strcmp(typeCode, @encode(NSRect)) == 0)
{
    return NSStringFromRect(*(NSRange *)value);
}
else if (strcmp(typeCode, @encode(NSRange)) == 0)
{
    return NSStringFromRect(*(NSRange *)value);
}

If I were cool, I’d use NSDictionarys to map from the @encode-string to an appropriate format string or function pointer. This is conceptually cleaner; less error-prone than using macros; and almost certainly faster. Unfortunately, it gets a little tricky with functions, since I need to deference value into the proper type.

One final note from my testing, I could do away with the strcmp()s, because directly comparing @encode string pointers (eg if(typeCode == @encode(NSString*)) works. I don’t know if it will always work though, so relying on it strikes me as a profoundly Bad Idea. But maybe that bad idea will give someone a good idea.

Limitations

Arrays

C arrays generally muck things up. Casting to a pointer works around this:

char x[14] = "Hello, world!";
//LOG_EXPR(x); //error: invalid initializer
LOG_EXPR((char*)x); //prints fine

__func__

Because it is a static const char [], __func__ (and __FUNCTION__ or __PRETTY_FUNCTION__) need casting to char* to work with LOG_EXPR. Because logging out a function/method call is something I do frequently, I use the macro:

#define LOG_FUNCTION()	NSLog(@"%s", __func__)

long double (Leopard and older)

On older systems, LOG_EXPR won’t work with a long double value, because @encode(long double) gives the same result as @encode(double). This is a known issue with the runtime. The top-level LOG_EXPR macro could detect a long double with if((sizeof(_X_) == sizeof(long double)) && (_TYPE_CODE_ == @encode(double))). But I doubt this will ever be necessary.

I haven’t actually written any code that uses long double, because I use NSDecimal, or another base-10 number format, for situations that require more precision than a double.

Scaling and Frameworks

Growing LOG_EXPR to handle every type is a lot of work. I’ve only added types that I’ve actually needed to print. This has kept the code manageable, and seems to be working so far.

The biggest problem I have is how to deal with types that are in frameworks that not every project includes. Projects that use CoreLocation.framework need to be able to use LOG_EXPR to print out CoreLocation specific structs, like CLLocationCoordinate2D. But projects that don’t use CoreLocation.framework don’t have a definition of the CLLocationCoordinate2D type, so code to convert it to a string won’t compile. There are two ways I’ve tried to solve the problem

Comment-out framework-specific code

This is pretty self-explanatory, I’ll fork VTPG_Common.m and un-comment-out code for types that my project needs to print. It works, but it’s drudgery. Programmers hate that.

Hardcode type info

The idea is to hard-code the string that @encode(SomeType) would evaluate to, and then (since we know how SomeType is laid out in memory) use casting and pointer-arithmetic to get at the fields.

For example:

//This is a hack to print out CLLocationCoordinate2D, without needing to #import <CoreLocation/CoreLocation.h>
//A CLLocationCoordinate2D is a struct made up of 2 doubles.
//We detect it by hard-coding the result of @encode(CLLocationCoordinate2D).
//We get at the fields by treating it like an array of doubles, which it is identical to in memory.
if(strcmp(typeCode, "{?=dd}")==0)//@encode(CLLocationCoordinate2D)
	return [NSString stringWithFormat:@"{latitude=%g,longitude=%g}",((double*)value)[0],((double*)value)[1]];

This Just Works in a project that includes CoreLocation, and doesn’t mess up projects that don’t. Unfortunately it’s horribly brittle. Any Xcode or system update could break it. It’s not a tenable fix.

Areas for Improvement

If there’s some type LOG_EXPR can’t handle that you need, please jump right in and improve it!

When I have time, I plan to write a general parser for @encode()-strings. This will let me print out any struct, which mostly solves the type-defined-in-missing-framework problem, and would let LOG_EXPR Just Work with types from all kinds of POSIX/C libraries.

Using LOG_EXPR() in Your Project

Download VTPG_Common.m and VTPG_Common.h from my github repository, and add them to your Xcode project.

Now just add the line #import "VTPG_Common.h" to your prefix file (named <ProjectName>_Prefix.pch by default), after the #ifdef __OBJC__, for example:

#ifdef __OBJC__
    #import <Foundation/Foundation.h>
    // maybe other files, depending on project  template...
    #import "VTPG_Common.h"
#endif

Now LOG_EXPR() will work everywhere in your project.

April 29, 2010

What Am I About To Call?

Filed under: Cocoa,iPhone,MacOSX,Objective-C,Programming,Reverse Engineering,Tips | , ,
― Vincent Gable on April 29, 2010

Say you’re in gdb, and about to execute a call instruction for dyld_stub_objc_msgSend, how do you know what’s about to happen?

On i386

(gdb) x/s *(SEL*)($esp+4)

tells you the message that’s about to be sent.

(gdb) po *(id*)$esp

tells you the target object that’s about to get the message.

October 12, 2009

Don’t Check malloc()

Filed under: C++,Cocoa,iPhone,MacOSX,Objective-C,Programming,Quotes,Tips | , , ,
― Vincent Gable on October 12, 2009

There’s no point in trying to recover from a malloc failure on OS X, because by the time you detect the failure and try to recover, your process is likely to already be doomed. There’s no need to do your own logging, because malloc itself does a good job of that. And finally there’s no real need to even explicitly abort, because any malloc failure is virtually guaranteed to result in an instantaneous crash with a good stack trace.

Mike Ash

This is excellent advice. Peppering your code with if statements harms readability and simplicity.

It’s still a good idea to check large (many MB) mallocs, but I can’t imagine recovering gracefully from a situation where 32 byte memory allocations are failing on a modern desktop.

December 12, 2008

Catching nil Member Variables

Filed under: MacOSX,Objective-C,Sample Code | , , ,
― Vincent Gable on December 12, 2008

When I wrote this method it seemed like it would be helpful,

//log a warning for any member variable that nil
- (void) warnOfNilIVars;
{
  unsigned int ivarCount = 0;
  Ivar * ivarList = class_copyIvarList([self class], &ivarCount);
  if(ivarList) {
    for(int i = 0; i < ivarCount; i++){
      const char *typeString = ivar_getTypeEncoding(ivarList[i]);
      if(typeString && typeString[0] == '@')
        if(!object_getIvar(self, ivarList[i]))
          NSLog(@"***WARNING: ivar %s of %@ is nil", ivar_getName(ivarList[i]), [self className]);
    }
    free(ivarList);
  }
}

But in practice I haven’t really used it (and when I have there were quite a few false-positives). Still, I think it’s pretty neat that you can do something like this in Objective-C.

If you find a use for it, please let me know!

December 4, 2008

NSAssert Considered Harmful

Filed under: Bug Bite,Cocoa,MacOSX,Objective-C,Programming,Tips | , , , ,
― Vincent Gable on December 4, 2008

The NSAssert() function is unreliable, and therefore dangerous. Plain-old assert() is a better choice.

Note that #define NDEBUG disables assert(); and #define NS_BLOCK_ASSERTIONS disables NSAssert(). One kind of assert being disabled does not mean the other kind is. Nether kind of assertion is disabled by default when building in “Release” mode — you must be sure to disable them yourself. Be careful, a user will not appreciate the difference between a bug and a failed assertion — both are a crash.

assert() immediately stops your program with a SIGABORT signal. You can’t miss a failed assert(), its behavior is predictable, and it fails fast (link is a PDF).

But a failed NSAssert() behaves unpredictably. Sometimes it stops program execution, sometimes it does not and leaves the program in a strange inconsistent state. I have entirely missed NSAssert()s failing, because I did not look at the console. if(!expr) NSLog(); would have been a better choice in the those cases, because at least I would have known how it behaved.

According to Apple,

Assertion macros, such as NSAssert and NSCAssert … pass a string to an NSAssertionHandler object describing the failure. Each thread has its own NSAssertionHandler object. When invoked, an assertion handler prints an error message that includes the method and class (or function) containing the assertion and raises an NSInternalInconsistencyException.

Complicated! The complexity means it is possible to customize what happens when an NSAssert() fails. That sounds cool, but I’ve never heard of someone needing to actually do that.

If a framework catches NSInternalInconsistencyExceptions, then your program will keep right on running after a failed NSAssert(). I have had this happen to me several times. I apologize for not having taken the time to investigate what frameworks were catching what.

Apple could change what catches what with any software update.

Variability and complexity are the last things you want while debugging. There’s no reason to invite it them by using NSAssert() over assert(). Since NSAssert() is not guaranteed to stop your program, it can not be relied on to guard against data corruption, or anything worse then a predictable crash.

UPDATE 2009-06-01: You can annotate assert(), so it prints an explanation like NSAssert(), by &&ing in a string after the condition. For example assert(i < j) is a lot more useful with an explanation: assert(i < j && "unexpected cycle in graph") — on failure it prints

Assertion failed: (i < j && “unexpected cycle in graph”), function WillHalt(), file /path/to/code.m, line 30.

December 2, 2008

Bug In The Machine

Filed under: Announcement,Programming,Reverse Engineering | , , ,
― Vincent Gable on December 2, 2008

I was going through some old (OS 9 old at youngest!) backups, and found this dump from a MacsBug session. You can actually see a bug sitting in memory! To me it looks like the bug is inside an old television, with distortion sweeping across the screen, so some of the bug’s left side is wrapped around to the right.

 Displaying memory from 1D9F8CB0
  1D9F8CB0  •???•???•???•???•???•???•???•???•???•???•???•???•???•???•???•???
  1D9F8CF0  •???•???•???•???•???•???•???•???•???•???•???•???•???•???•???•???
  1D9F8D30  •???•???•???•???•???•???•???•???•???•???•???•???•???•???•???•???
  1D9F8D70  •???•???•???•???•üÖ`´••••••P•••Ñ•y•pÄ@•••••Ä•@•••••••••H•••H••••
  1D9F8DB0  •••••••••••É9ÿ•••••••••••••••••••üçÄ´••••• •••‚ 0000000000000000
  1D9F8DF0  0000000000000000000000000000000000000000000000000000000000000000
  1D9F8E30  0000000000000000000000000000000000000000000000000000000000000000
  1D9F8E70  0000000000000000000000000000000000000000000000000000000000000000
  1D9F8EB0  0000000000000000000000000000000000000000000000000000000000000000
  1D9F8EF0  0000000000000000000000000000000000000000000000000000000000000000
  1D9F8F30  0000000000000000000000000000000000000000000000000000000000000000
  1D9F8F70  0000000000000000000000000000000000000000000000000000000%***$*#**
  1D9F8FB0  #*$$*$*#*#**$$*00*$*$*$$**#*#*#*#*#*#**#%00000000000000*•!#•!!!!
  1D9F8FF0  !!!•!!!!!!!!!!!0%!!!!#!!!!!!!!!!!!!!!!!!*00000000000000*!"!•!!!!
  1D9F9030  !!••!!!!!!!!!!!0.!!••!!!!!!!!!!•!•!!•!!!#00000000000000*!!!(•!!!
  1D9F9070  !!!(•!!!!!!!!!!0%!!((!!!!!!!!•!!!!!!!!!!*00000000000000#!!! ••! 
  1D9F90B0  •••••••••••••••0*••!•••••••••••••••••••••00000000000000*!!!•••00
  1D9F90F0  •••••••••••••••0#••!•••••••••••!00•••••••00000000000000#!!!••#0/
  1D9F9130  0&••••••••••••••!••! •••••••••+0$0.••••••00000000000000#!!!•!0••
  1D9F9170  !00#••••••••!000000$!•••••••!00!••,••••• 00000000000000*!!!•0%••
  1D9F91B0  ••%0%••••••%00000000/••••••,0.••••%0•••••00000000000000$!••00•••
  1D9F91F0  ••!*00••••000000000000••••00!••••••00#•••00000000000000*!!/0••••
  1D9F9230  ••!••/0$•00000000000000••0$••••••••*0%!•!00000000000000#•#0$••••
  1D9F9270  •!•••!$•*00000000000000%•!•••••••!(•*0*• 00000000000000*•00••••!
  1D9F92B0  (•••••••0000000000000000•••••••!* •••%0•!00000000000000*00••••!0
  1D9F92F0  &!•••••/00000000000000000!•••'$00!••••00*00000000000000*•*!•••00
  1D9F9330  000••••000000000000000000•!•*00/%0••••••*00000000000000$!•!••$0•
  1D9F9370  ••%00•!000000000000000000*$00%!••0#••••••00000000000000$!••••0.•
  1D9F93B0  ••••$•%0000000000000000000•%!•••••0!••••!00000000000000*!•(•00••
  1D9F93F0  •••(••00000000000000000000!! •••••/0"••••00000000000000#!!!$0•••
  1D9F9430  ••!!••00000000000000000000•!!••••!#0%!•••00000000000000*!!*0*•••
  1D9F9470  ••!•••00000000000000000000*!•••("!••0#•••00000000000000*!!0,••••
  1D9F94B0  •! •••00000000000000000000#!•!•!••••/0•• 00000000000000#!%0!••••
  1D9F94F0  ••••••00000000000000000000#•*••••••••0%!!00000000000000*!•$•••%0
  1D9F9530  0000#•00000000000000000000#%00000%•••!•!•00000000000000*!!!••!0%
  1D9F9570  *•! ••00000000000000000000*#*$!!•0!•••••*00000000000000*!(!••00!
  1D9F95B0  ••••••00000000000000000000••!!•••/0•••••#00000000000000#!!!•$0••
  1D9F95F0  ••••••%0000000000000000000!(!!••••0%•••••00000000000000*!!!(0*••
  1D9F9630  ••!!!!.000000000000000000.!!!••!!!%0-!••!00000000000000*!(•0,•••
  1D9F9670  ••!•!•!000000000000000000•#*•••••••,0••••00000000000000$!!$0••••
  1D9F96B0  •!•$00•/00000000000000000•00/*••••••0.!•!00000000000000*!!!!••••
  1D9F96F0  #000•••!0000000000000000$(•.00%••••• •!!•00000000000000*!!••••••
  1D9F9730  00!•••••/000000000000000!!(!!*00•••••• !•00000000000000$!!(••••$
  1D9F9770  0•••••••!00000000000000*!!!!!•!0!••••••!$00000000000000#(!!••••0
  1D9F97B0  ,••••••!!$000000000000$!!!!!!•!00•••••••$00000000000000*!!!••••0
  1D9F97F0   ••••!••••!0000000000$(!!!!!••••0*••••••!00000000000000#!!!•••,/
  1D9F9830  ••••!!!•••••00000000*!!!!"!!••••/0••••••!00000000000000*!!!•••0!
  1D9F9870  ••••••••••••••$%%%•!!!!!!!!••••• 0)••••••00000000000000#!!!••/0•
  1D9F98B0  •••!!•••••••••.,/•(•!!!!!!•••••••0,!!••••00000000000000*!!!••0!•
  1D9F98F0  •••••••••••••00000•!!!!!!••••••••!0!!••••00000000000000#!!!•••••
  1D9F9930  ••!!••••••••.000000!!!!!••••••••••••!!••!00000000000000$••!•••••
  1D9F9970  ••!•••••••••0000000!• •••••••••••••••!•••00000000000000*!•!•••••
  1D9F99B0  •!!•••••••••0000000$!••••••••••••••••!!•!00000000000000#!!!•••••
  1D9F99F0  •!•••••••••!0000000*!"••••••••••••••••!• 00000000000000#!!••••••
  1D9F9A30  ••••••••••••0000000#!!••••••••••••••••••!00000000000000#!!!•••••
  1D9F9A70  ••••••••••••0000000•!!!••••••••••••••••••00000000000000$!"••••••
  1D9F9AB0  •••••••••••••000000!!!••••••••••••••••••!00000000000000*!!!•••••
  1D9F9AF0  •••••••••••••/0000•(!!!••••••••••••••••• 00000000000000#!•!•••••
  1D9F9B30  ••••••••••••••!*$•!!!!!••••••••••••••••••00000000000000#!!••••••
  1D9F9B70  ••••••••••••••••!!!"!!!••••••••••••••••••00000000000000*!!!•••••
  1D9F9BB0  •••••••••••••••••!!!!!•••••••••••••••••••00000000000000%$#*!!•! 
  1D9F9BF0  •!!!•• ! •! • • !#*$*#!!!•! !! !•• ! • !*00000000000000000000000
  1D9F9C30  0000000000000000000000000000000000000000000000000000000000000000
  1D9F9C70  0000000000000000000000000000000000000000000000000000000000000000
  1D9F9CB0  0000000000000000000000000000000000000000000000000000000000000000
  1D9F9CF0  0000000000000000000000000000000000000000000000000000000000000000
  1D9F9D30  0000000000000000000000000000000000000000000000000000000000000000
  1D9F9D70  0000000000000000000000000000000000000000000000000000000000000000
  1D9F9DB0  0000000000000000000000000000000000000000000000000000000000000000
  1D9F9DF0  0000000000000000000000000000000000000000000000000000000000000000
  1D9F9E30  0000000000000000000000000000000000000000000000000000000000000000
  1D9F9E70  0000000000000000000000000000000000000000000000000000000000000000
 Unrecognized symbol 'prinf'
 Closing log

I swear this is a real memory dump of a sold-for-money application that I didn’t have any part in writing. Unfortunately I don’t remember some specifics, like which application it was.

September 27, 2008

CFShow is NSLog for Core Foundation Types

Filed under: MacOSX,Programming,Tips | , , , ,
― Vincent Gable on September 27, 2008

CFShow(coreFoundationThingy) will print out a description of coreFoundationThingy to the console. Output looks something like:

{value = w:1186.000000 h:687.000000 type = kAXValueCGSizeType}

If NSLog() is printing something out as an NSCFType, try CFShow().

September 18, 2008

I Would Rather Have a Runtime Error Than a Compile Error

Filed under: Programming,Quotes,Reverse Engineering | , , ,
― Vincent Gable on September 18, 2008

And the weird thing is, I realized early in my career that I would actually rather have a runtime error than a compile error. [(some laughs)] Because at that time… now this is way contrary to popular opinion. Everybody wants early error detection. Oh God, not a runtime error, right? But the debugger gives you this ability to start poking and prodding, especially in a more dynamic language, where you can start simulating things, you can back it up… You’ve got your time-machine debuggers like the OCaml one, that can actually save the states and back up.

You’ve got amazing tools at your disposal (in the debugger)… Whereas if the compiler gives you an error that says “expected expression angle-bracket”, you don’t have a “compiler-debugger” that you can shell into…

So, you know, in some sense, your runtime errors are actually kind of nicer.

— An excerpt from one of Steve Yegge’s (long!) talks.

I think there is a real nugget of truth in this. At runtime, you can examine your program’s state, but there is absolutely no way to do that at compile time. Without a debugger, you can’t just look at some nontrivial code and know what the value of x is when there’s an error reading y. (Adding "print x", recompiling, and trying again, would work of course, but that’s just using your compiler as an inefficient debugger!)

Similarly, Strong Typing vs. Strong Testing , essentially argues that some tests can only be made at runtime. (If you read any links on this page, read it, it’s much shorter and to the point).

September 16, 2008

-description, Little-Known Hero of Debugging

Filed under: Cocoa,MacOSX,Objective-C,Programming,Sample Code,Tips | , , ,
― Vincent Gable on September 16, 2008

Or: How to Make NSLog() Useful With Your Objects

Say you have an archaically named NSArray that you want to inspect — it’s easy to do, since NSLog(@"The bestiary is %@", bestiary); prints out the array’s contents

2008-09-16 19:46:06.445 Tester[2678:10b] The bestiary is (
Cheetah,
Pumpa,
Jaguar,
Panther,
Tiger,
Leopard
)

But if you try to NSLog your own object, you get pretty useless output, like

myObject = <MyObject: 0x53f330>

Fortunately, it’s easy to fix! Just implement the method -(NSString*) description; and whatever it returns will be printed by NSLog and GDB (po object, will print object in GDB and the Xcode debugging console).

Here’s an (unfortunately complex) example,

- (NSString*) description;
{
  return [NSString stringWithFormat:@"<%@ %p> = {\n\tquestion=%@,\n\tanswer=%@,\n\tsource=%@\n}", [self className], self, self.question, self.answer, self.source];
}

output:

myObject = <MyObject 0x53eed0> = {
  question=What is the Best Thing Ever Of All Times, Ever?,
  answer=The Internet!,
  source=http://www.cabel.name/2008/01/2007-cabel-yay-awards.html
}

Useful Formatters and such

These macros have made my debugging-life easer.

%p tells NSLog to print the address of a pointer.

-className returns gives the name of a class as an NSString.

Don’t manually print out a Cocoa struct, ever, there are already NSStringTo* functions to do that for you, like NSStringFromPoint().

NSStringFromSelector() works as advertized (and paired with NSSelectorFromString() is very useful in general).

%lld tells NSLog to print a long long (64-bit integer). See also, printf reference.

%Lf tells NSLog to print a long double. See also, printf reference.

Best Practices

Whenever you make a new object, I strongly recommended immediately implementing a description method, and religiously keeping it up to date (it’s not hard, honest!). This won’t fix bugs, but it will make finding some of them much easier.

August 5, 2008

Simplified Logging

Filed under: Cocoa,MacOSX,Objective-C,Programming,Sample Code,Tips,Usability | , ,
― Vincent Gable on August 5, 2008

I have noticed a pattern in my Cocoa code, which I have been able to simplify. I often print out the value of a variable for debugging. 99 times out of 100, the code looks like this: NSLog(@"actionURL = %@", actionURL);, where actionURL is some variable.

But using the macros, I can say LOG_ID(actionURL);. This is shorter, and non-repetitive.

The macros I use to simplify debugging (2008-09-16):

#define FourCharCode2NSString(err) NSFileTypeForHFSTypeCode(err)

#define LOG_4CC(x) NSLog(@"%s = %@", # x, FourCharCode2NSString(x))
#define LOG_FUNCTION() NSLog(@"%s", __FUNCTION__)
#define LOG_ID(o) NSLog(@"%s = %@", # o, o)
#define LOG_INT(i) NSLog(@"%s = %d", # i, i)
#define LOG_INT64(ll) NSLog(@"%s = %lld", # ll, ll)
#define LOG_FLOAT(f) NSLog(@"%s = %f", # f, f)
#define LOG_LONG_FLOAT(f) NSLog(@"%s = %Lf", # f, f)
#define LOG_OBJECT(o) LOG_ID(o)
#define LOG_POINT(p) NSLog(@"%s = %@", # p, NSStringFromPoint(p))
#define LOG_RECT(r) NSLog(@"%s = %@", # r, NSStringFromRect(r))
#define LOG_SIZE(s) NSLog(@"%s = %@", # s, NSStringFromSize(s))

Look in assert.h for insight on how to roll your own debugging macros.

Older Posts »

Powered by WordPress