May 17 2010
class-dump: Dumping Classes
class-dump path_to_executable
If the tool nm and its wealth of information seems impressive, class-dump is amazing. Feed it an executable and get back a complete list of class names, internal variables, and methods, even as a set of header files, thanks to its deep understanding of Objective-C and Mach-O. Apple’s product might be stripped of their symbols, but class-dump can list their internal structure.
I created a template application, added a method (shouldShowInClassDump), and built it with all the symbols stripped out. Running class-dump on the resulting Release executable revealed the following:
/*
* Generated by class-dump 3.3.1 (64 bit).
*
* class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2009 by Steve Nygard.
*/
#pragma mark -
/*
* File: build/Release/SampleApp.app/Contents/MacOS/SampleApp
* Arch: Intel x86-64 (x86_64)
*
* Objective-C Garbage Collection: Unsupported
*/
@interface SampleAppAppDelegate : NSObject
{
NSWindow *window;
}
- (void)applicationDidFinishLaunching:(id)arg1;
- (BOOL)shouldShowInClassDump:(id)arg1;
@property NSWindow *window; // @synthesize window;
@end
To see how applications compared in aggregate, I wrote a simple ruby script to walk through /Applications and sum up the number of lines of headers from class-dump and the number of @interface declarations. Both are crude measures of complexity.
Top 10 by Header Lines
37968 iPhoto
37053 Aperture
21228 iWeb
19307 Keynote
16564 iMovie
16235 Coda
15625 Keynote
15111 Grapher
14735 Pages
14724 iCal
Top 10 by Interfaces
1203 Aperture
1066 iWeb
1004 Grapher
967 iPhoto
859 Path Finder
748 Keynote
730 iMovie
664 LaunchBar
649 iCal
646 Coda
Top 10 by Lines Per Interface
39.54 Retrospect
39.33 Grab
39.26 iPhoto
37.72 MemoryMiner
37.28 Papers
35.72 iTunes
35.65 Sequel Pro
34.71 Console
32.72 iChat
32.38 GarageBand
Apple’s tools are clearly complex, with iPhoto and Aperture leading by almost a factor of two in header lines, but apps like Panic’s Coda hold their own. And Retrospect topping the final list is obviously an indictment of my coding abilities.
Ruby Script
#!/usr/local/bin/ruby19
# analyze_apps.rb
# * Analyze dump from 'class-dump path_to_executable' to collect statistics: Interfaces, Lines
# Collect app paths.
CLASS_DUMP_PATH = "/usr/local/bin/class-dump"
apps = []
apps_folders = ["/Applications", "/Applications/iWork '08", "/Applications/iWork '09", "/Applications/Microsoft Office 2004", "/Applications/Microsoft Office 2008", "/Applications/Utilities"]
apps_folders.each do |app_folder|
app_list = Dir.entries(app_folder)
app_list.each do |app|
apps << "#{app_folder}/#{app}"
end
end
# Loop through apps.
apps.each do |app|
# Check that the app exists.
if Dir.exists?("#{app}") && app.include?(".app")
app_array = app.split("/")
app_name = app_array[app_array.count-1].split(".app")[0]
exe_path = "#{app}/Contents/MacOS/#{app_name}"
# Check that the executable exists.
if File.exists?(exe_path)
# Use 'classdump' to dump Mach-O header information.
results = %x[#{CLASS_DUMP_PATH} "#{exe_path}"].split("\n")
line_count = results.count
interface_count = 0
results.each do |line|
if line.include?("@interface ")
interface_count = interface_count + 1
# puts "#{app_name}: #{line}"
end
end
puts "#{app_name},#{app},#{interface_count},#{line_count}"
end
end
end