Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSDictionary `description` formatting problem -- treats structure like char data

I've got a custom class (which resembles an NSArray in concept and, hopefully, formatted appearance) which has a description formatter.

When the output of the formatter is printed (NSLog) by itself it looks fine, but when it's included as an element of an NSDictionary description the NSDictionary formatter seems to decide that it's a character string, not a structure definition, encloses it in quotes, and escapes all of the control characters in the string.

It doesn't do this for a standard NSArray, of course, so I'm wondering how it decides to treat the string one way vs the other.

Eg, rather than having output that looks like:

theChildren = (
                  {
                      "@meta.type" = "ns3:location";
                      "childNumber" = 0;
                      ...

It looks like:

theChildren = "( \n\t\t {\n\t  \"@meta.type\" = \"ns3:location\";\n\t   \"childNumber\" = 0;
...

Can anyone suggest a way to alter this behavior?

FWIW, I accumulate the description string (with data consisting primarily of the results from calls to NSDictionary description) in an NSMutableString, then do NSString stringFromString at the exit (though I don't know that that does anything).

Added

I think I found the answer (haven't checked yet) buried in the NSDictionary writeup:

Discussion

The returned NSString object contains the string representations of each of the dictionary’s entries. descriptionWithLocale:indent: obtains the string representation of a given key or value as follows:

If the object is an NSString object, it is used as is.

If the object responds to descriptionWithLocale:indent:, that

method is invoked to obtain the object’s string representation.

If the object responds to descriptionWithLocale:, that method is

invoked to obtain the object’s string representation.

If none of the above conditions is met, the object’s string

representation is obtained by invoking its description method.

Update

Well, turns out they lie. I implemented descriptionWithLocale:indent: and it never gets called.

Update 9/23

Interestingly, if I make my class a subclass of NSArray then NSDictionary calls descriptionWithLocale:indent: and it formats correctly. Sounds like NSDictionary is "cheating" and testing isKindOfClass rather than respondsToSelector, or else is just prejudiced against non-NS stuff.

It's kind of ugly to have to subclass NSArray, though, in terms of acquiring a lot of behaviors I don't want to mimic, and carrying extra unused data.

Etc

Another option is to convert the escaped string back to its original. This takes a 31-line procedure to handle the basics (\n, \t, \", and \\). The up-side is that I don't need to subclass NSArray. The main downside is that this routine must be inserted in any NSLog call that could display my class. Another minor downside is that the escaped strings were wrappered with quote characters I can't eliminate, but that's hardly noticeable.

Got it #ifdefed for now -- not sure which I'll pick.

like image 628
Hot Licks Avatar asked Sep 22 '11 21:09

Hot Licks


2 Answers

This is a wonderful security-related "feature" that was introduced in OS X 10.5+ version of syslog().

As explained by an Apple engineer in this post: Who broke NSLog on Leopard?,

That's the behavior of syslog(). From the man page:

Newlines and other non-printable characters embedded in the message string are printed in an alternate format. This prevents someone from using non-printable characters to construct misleading log messages in an output file. Newlines are printed as "\n", tabs are printed as "\t". Other control characters are printed using a caret ("^") representation, for example "^M" for carriage return.

The ASL subsystem, which NSLog() writes to, does the same (at least in Leopard). Writing the XML to a file is a reasonable alternative.

Chris Kane Cocoa Frameworks, Apple

See man syslog for more info.

like image 80
NSGod Avatar answered Nov 15 '22 19:11

NSGod


There's no real answer to this question (the "security feature" of OS X doesn't appear to affect iOS console writes), but there are these two work-arounds:

#1: Interestingly, if I make my class a subclass of NSArray then NSDictionary calls descriptionWithLocale:indent: and it formats correctly. Sounds like NSDictionary is "cheating" and testing isKindOfClass rather than respondsToSelector, or else is just prejudiced against non-NS stuff.

It's kind of ugly to have to subclass NSArray, though, in terms of acquiring a lot of behaviors I don't want to mimic, and carrying extra unused data. Etc

#2: Another option is to convert the escaped string back to its original. This takes a 31-line procedure to handle the basics (\n, \t, \", and \). The up-side is that I don't need to subclass NSArray. The main downside is that this routine must be inserted in any NSLog call that could display my class. Another minor downside is that the escaped strings were wrappered with quote characters I can't eliminate, but that's hardly noticeable.

(Accepted this answer, even though it's not the "real" answer, because my accepted % would suffer otherwise. I ask too many difficult questions, I guess.)

like image 40
Hot Licks Avatar answered Nov 15 '22 19:11

Hot Licks