[Edit: ecto ate this post, so I’m typing it in again!]
I discovered that a lot of applications will unnecessarily write to NSUserDefaults. This causes your app to hit the disk when it shouldn’t, and is a slight performance penalty. AppKit is also susceptible to this problem; if you hit cmd-O to bring up the open panel in any application, you will see it writing things to the user defaults, when it probably doesn’t need to do so. I’m working on fixing that, and you should to!
So, how do you do it? It is easy — just add a breakpoint on -[NSUserDefaults(NSUserDefaults) setObject:forKey:]. You can do this with gdb:
b -[NSUserDefaults(NSUserDefaults) setObject:forKey:]
Or you can use the breakpoints window in Xcode (my preferred way):
Then, reproduce whatever action might cause it to happen (ie: starting your application, or in my test case, cmd-o to bring up the open panel). Look at the backtrace in Xcode and figure out why you are doing too much work:
Also, a special thanks to Mark for logging bugs against me :)
It should only be hitting disk when -[NSUserDefaults synchronize] is called, shouldn’t it? App developers should be able to safely assume that writing the defaults is cheap and efficient, and should be callable all the time with essentially the cost of writing to a dictionary.
If NSUserDefaults is hitting the disk before synchronize is called, It would be a better use of time to fix it so that it fulfills the contract (if it, in fact, doesn’t), than to encourage developers everywhere to work around a system performance issue by changing their own code.
Daniel — yes, you are right — I left off the part about NSUserDefaults lazily writing to the disk. The whole point is to avoid that disk hit. Writes are cheap and efficient, but why write at all when you don’t have to?
This is a very minor performance issue. More often than not, other things will cause a slowdown beforehand.
Hmm, I think this is a prime example of ‘premature optimization’. Anno 2008, ‘hitting the disk’ is dirt cheap (in fact, I believe it even gives back change nowadays…). How long does it take to save the defaults file? (you measured this before taking the time to optimize, of course ;-))
According to the documentation -[NSUserDefaults synchronize] is called automatically at set intervals, and in my experience it seems that the plist file is updated automatically within a very short time of changing a preference (either in code or as a result of resizing a window frame…). If you are going to write big blocks of data; those shouldn’t be in the user defaults anyway.
Patrick — you are very right. It is a small optimization, but I wouldn’t call it premature. It was established after looking at applications and the disk activity that they do when launching, or doing certain activities. In day to day programming, it isn’t something to really worry about. If you consider the particular case I’m attempting to make faster, which is the bring up of the open or save panel, then it makes sense to avoid unnecessary disk hits. However, there are other factors that slow the panel down; one such case is the spin up of disks to show the eject icon in the sidebar; the time to do this operation overshadows many of the other things. That’s something that should be fixed first.
So when it comes to NSUserDefaults, do people consider the defaults domain part of the “user interface” of the application? My opinion often wanders between “yes, power users could enable hidden features through the defaults interface” and “the user shouldn’t touch it, but if they break it I should be able to carry on” but never gets as far as “no”. I’d be interested to hear what other people think :-)
Corbin – I still don’t really believe that reading or writing a 4k or 8k text file now and again is going to have an impact on the user experience. If users choose to spin down the drive that contains their home directory (if this would really slow down the drop down speed), well, it’s their decision…
There is (or at least was) a global setting (in the user defaults of course!) that effects the sheet animation speed . Not sure if you could (you probably can) or should override this on a per application basis.
Could it be possible to put back the ability to see invisible items in NSOpenPanel/NSSavePanel while devastating this part of the AppKit?
Patrick — other considerations are things like network home directories, which some people at apple still use (I occasionally use mine for various testings or when logging onto other machines). Still, it is a minor optimization, and I still think it is good practice to not right out things you don’t need to.
It is sort of akin to writing out things into a Delphi form (a related but similar example). Why right out things you don’t need to? For this purpose, we had default values that could be used (and not written out) when the form was saved to disk. This increased load time for the forms, and avoided having too much clutter in them.
But really, it is a minor optimization. People can take it or leave it.
Stephane: From: http://developer.apple.com/releasenotes/Cocoa/AppKit.html
“NSSavePanel and NSOpenPanel can once again show hidden files by setting a user default. For instance you can use ‘defaults write -g AppleShowAllFiles 1’ to display hidden files in the panels.”.
I am also aware that people want more control over this, and will consider making other changes.
leeg: Basically, if you publish user default values (or document them), then it becomes acceptable for the user to manipulate them. Xcode has many advanced options that can only be set with user defaults, and we document quite a few of them. It is unfortunate that there isn’t UI for a lot of the things, but sometimes too much UI makes things too cluttered.
thanks!
“NSSavePanel and NSOpenPanel can once again show hidden files by setting a user default.”
Why is is not listed in the 300+ new features on the Mac OS X front page? There should even be a PC/Mac ad for this.
Release Notes is such a modest spot to list the return of this feature that I didn’t find it when I read them quickly a long time ago.