This article has been updated, and moved here
Archive for the ‘Tips’ category
Actually I do have one big issue with the article:
If there’s one behavior of your application that you should focus on eliminating, it’s the behavior of crashing. Above all other “nuisances,” this is the one that is absolutely unacceptable.
But preserving someone’s data is more important then crashing. Having to rewrite your paper because your PC devoured it is worse then crashing. Crashing may be the worst “nuisance”, but there are more important bugs to squash first. However that is a topic for another time — we all agree that crashes are a problem should be fixed.
Although Daniel shows how to synthesize debugging symbols from hex-address, I think it’s worth considering leaving debugging symbols in your shipping app.
The reasons for [building applications without debugging symbols] are mostly to keep the shipping application as small as possible, but there are also some good reasons to hide the symbols if they reveal too much about your business strategy.
I can’t say anything about your business strategies, but removing information that can help you diagnose problems “in the field” seems like a very bad trade-off for slightly smaller files.
Hard-drives cost about $0.30 per gigabyte (GB), and the price is still falling fast*. Because the GB is the unit hard-disks are sold by, I am going to use it instead of MB or KB; I think it puts file-sizes into the right perspective.
Today’s applications are “big”, for a very good reason. That article says it better then I can, but the gist of it is that megabytes are cheaper then air and bigger programs can do more, making them more useful (the cost of a GB of storage space has fallen over 20 fold since the article was written, by the way).
The largest program I use every day that was built with debugging symbols on is EyeTV. It weighs in under 0.11 GB, and I don’t consider that “bloated”, because I get good value for my 3 cents of disk-space. Stripping debugging symbols with
strip makes it 0.0076 GB smaller. That translates into $0.002 worth of hard disk, that could store 13.7 seconds of TV . And that is insignificant. A few thousandths of a GB make little difference, and that’s all stripping debugging symbols will get you.
Of course, this is all academic if no one ever sees the crash-logs. Unfortunately, developers know, that’s the current crappy state of affairs. Crash reports are sent to Apple, and only Apple. The developers who wrote the program — the ones who could best fix the problem, and who desperately want to know about it, are completely out of the loop.
If Apple passed crash logs on to developers, everyone would win. Developers would be able to squash more bugs in less time. Users would have a better, more productive and bug free, experience. Apple could sell those improvements. Microsoft already does this, and it seems to work well for them. Most people are unaware of this SNAFU, and probably think that reporting the crash to Apple gets the information to the right people. I don’t know if educating people about the issue would light a fire under Apple, but it might.
If enough people start using flash memory over current magnetic-platter harddrives, then the price-per-GB ratio could change, because flash is currently about 100x more expensive (per GB). But the trend of the current storage-medium’s price exponentially falling will continue
But by the time flash-based computers become popular, their cost-per-GB will probably be as good, or better, then full-sized hard drives of today. Tiny hard-drives, using conventional magnetic platters, like the ones in the iPod, are also a compelling alternative to flash.
When writing a short AppleScript to complete a specific task, do not write a procedure that checks if A exists, then operates on it if it does. Instead, write an expression that operates on A, and ignore any exceptions caused by A not existing. Surrounding the statement in a
try-block will ignore any exceptions it generates. The
whose commands can help. This almost always leads to cleaner, and faster, AppleScripts.
Here is an example, a re-write of code in EyeTV version 2.5 (if you have it, you can find this script at EyeTV.app/Contents/Resources/English.lproj/EyeTV\ Help/openreadme.scpt).
When run, this opens a readme inside the EyeTV application bundle, iff the EyeTV application is running. I did not write this, and I do not know exactly how it is used, or what constraints it was written under — just what it does.
set readmePath to "Contents:Resources:English.lproj:EyeTV Help:eyetvreadme.rtfd"
tell application "Finder"
set allApps to every application process
repeat with i from 1 to number of items in allApps
set thisApp to item i of allApps
if creator type of thisApp is "EyTV" then
open file (((application file of thisApp) as string) & readmePath)
Re-writing the code by making it an expression that assumes the correct
application process exists has a dramatic difference.
tell application "Finder"
set theEyeTVProcessBundle to file of first item of (every application process where creator type is "EyTV") as string
open (theEyeTVProcessBundle & "Contents:Resources:English.lproj:EyeTV Help:eyetvreadme.rtfd") as alias
We no longer need
repeat blocks — they are procedural constructs that were used to find the correct application process, and guard against trying to open a file that does not exist. (I also removed the variable
readmePath because I think the a path to “eyetvreadme.rtfd” is clearly a readme-path).
But it gets better. We could try removing the
try blocks as well, and just ignore any errors the interpreter complains about. That brings the the script down to four lines. We can make it one-line:
tell application "Finder" to open ((file of first item of (every application process where creator type is "EyTV") as string) & "Contents:Resources:English.lproj:EyeTV Help:eyetvreadme.rtfd") as alias, but I think that makes the script too hard to understand, because the name “theEyeTVProcessBundle” documents the long expression needed to reliably find the application bundle of the running process.
One line or four lines, that’s still a big reduction from 13 lines. Now it’s short enough to embed directly in Cocoa code with NSAppleScript.
Here’s another example. This is from IMLocation‘s uninstaller code, it It removes the IMLocationHelper from the list of items that will be opened at login. Here’s the original code:
tell application "System Events"
if (login item "IMLocationHelper") exists then
delete login item "IMLocationHelper"
Applying the approach of re-writing the procedure as an expression
tell application "System Events" to delete (login item "IMLocationHelper")
Taking things a step further, by removing the
try-block, and just ignoring errors when running the one-line script:
tell application "System Events" to delete (get login item "IMLocationHelper"). This makes a big difference in the clarity of the surrounding Cocoa code.
AppleScript is a pretty poor language on it’s own. Usually I see it being used to get some specific thing done. In other words, generally AppleScripts are essentially expressions, used to get or set a single value; rarely are they sub-programs. This is whyExpressions are such a good fit.
Here’s a helpful MacOSX feature that I finally stumbled upon. If you hold option down while selecting text, you can select an arbitrary rectangle, instead of some number of rows. A picture is worth a lot of words:
This is great for removing extraneous information from console logs (pictured), or copying just what you want. Unfortunately, it does not work in all applications, including Safari and Terminal. A damn shame, but it works like a charm with Xcode, Console, and TextEdit.