Since poseAsClass: is doomed anyway and appears to cause a crash on PowerPC[1], replace it with method-swizzling. This definitely contributes to our 64-bit cleanliness; hopefully, it also fixes that crash.
authorPeter Hosey <hg@boredzo.org>
Sat Jun 06 21:30:17 2009 -0700 (2009-06-06)
changeset 42113be8b67aac72
parent 4210 c36ddc8e6b22
child 4212 8925d6b9adcf
Since poseAsClass: is doomed anyway and appears to cause a crash on PowerPC[1], replace it with method-swizzling. This definitely contributes to our 64-bit cleanliness; hopefully, it also fixes that crash.

[1]: <http://groups.google.com/group/growldiscuss/msg/2b85001f4c6c0d4a>
Extras/GrowlMail/GrowlMailPreferences.m
     1.1 --- a/Extras/GrowlMail/GrowlMailPreferences.m	Sat Jun 06 21:30:17 2009 -0700
     1.2 +++ b/Extras/GrowlMail/GrowlMailPreferences.m	Sat Jun 06 21:30:17 2009 -0700
     1.3 @@ -35,16 +35,63 @@
     1.4  #import "GrowlMailPreferences.h"
     1.5  #import "GrowlMailPreferencesModule.h"
     1.6  #import "GrowlMail.h"
     1.7 +#import "GrowlMailNotifier.h"
     1.8 +
     1.9 +#import <objc/objc-runtime.h>
    1.10 +
    1.11 +static void GMExchangeMethodImplementations(Method a, Method b);
    1.12 +
    1.13 +@interface NSPreferences (GMSwizzleSticks)
    1.14 +
    1.15 ++ (id) sharedPreferencesForGrowlMail;
    1.16 ++ (id) sharedPreferencesFromAppKitSwizzledByGrowlMail;
    1.17 +
    1.18 +@end
    1.19  
    1.20  @implementation GrowlMailPreferences
    1.21 -// we need to do posing as the other mail bundles do that too
    1.22 +
    1.23 +//As of Mac OS X 10.5.6, Mail creates the +sharedPreferences object lazily, so the simplest way to install our prefpane is to swizzle the +sharedPreferences method.
    1.24 +//We used to install our prefpane by posing as NSPreferences, but class-posing doesn't exist in 64-bit, and seemed to cause at least one crash on PowerPC machines in GrowlMail 1.1.5b1.
    1.25  + (void) load {
    1.26 -	[GrowlMailPreferences poseAsClass:[NSPreferences class]];
    1.27 +	Class NSPreferencesClass = NSClassFromString(@"NSPreferences");
    1.28 +	if (!NSPreferencesClass)
    1.29 +		GMShutDownGrowlMailAndWarn(@"Couldn't install GrowlMail prefpane: NSPreferences class missing");
    1.30 +	else {
    1.31 +		//+[NSPreferences sharedPreferences]
    1.32 +		Method sharedPreferencesFromAppKit = class_getClassMethod(NSPreferencesClass, @selector(sharedPreferences));
    1.33 +		if (!sharedPreferencesFromAppKit)
    1.34 +			GMShutDownGrowlMailAndWarn(@"Couldn't install GrowlMail prefpane: +[NSPreferences sharedPreferences] method missing");
    1.35 +		else {
    1.36 +			//+[GrowlMailPreferences sharedPreferencesFromAppKitSwizzledByGrowlMail]
    1.37 +			Method sharedPreferencesFromAppKitFromGrowlMail = class_getClassMethod(self, @selector(sharedPreferencesFromAppKitSwizzledByGrowlMail));
    1.38 +			//+[GrowlMailPreferences sharedPreferencesForGrowlMail]
    1.39 +			Method sharedPreferencesForGrowlMail = class_getClassMethod(self, @selector(sharedPreferencesForGrowlMail));
    1.40 +
    1.41 +			//Follow the lady!
    1.42 +			GMExchangeMethodImplementations(sharedPreferencesFromAppKit, sharedPreferencesFromAppKitFromGrowlMail);
    1.43 +			GMExchangeMethodImplementations(sharedPreferencesFromAppKit, sharedPreferencesForGrowlMail);
    1.44 +			/*Results of the swizzling:
    1.45 +			 *
    1.46 +			 *+[NSPreferences sharedPreferences]
    1.47 +			 *	implemented by former +[NSPreferences sharedPreferencesForGrowlMail]
    1.48 +			 *
    1.49 +			 *+[NSPreferences sharedPreferencesForGrowlMail]
    1.50 +			 *	implemented by former +[NSPreferences sharedPreferencesFromAppKitSwizzledByGrowlMail] (the stub)
    1.51 +			 *
    1.52 +			 *+[NSPreferences sharedPreferencesFromAppKitSwizzledByGrowlMail]
    1.53 +			 *	implemented by former +[NSPreferences sharedPreferences]
    1.54 +			 */
    1.55 +		}
    1.56 +	}
    1.57  }
    1.58  
    1.59 -+ (id) sharedPreferences {
    1.60 +@end
    1.61 +
    1.62 +@implementation NSPreferences (GMSwizzleSticks)
    1.63 +
    1.64 ++ (id) sharedPreferencesForGrowlMail {
    1.65  	static BOOL	added = NO;
    1.66 -	id preferences = [super sharedPreferences];
    1.67 +	id preferences = [self sharedPreferencesFromAppKitSwizzledByGrowlMail];
    1.68  
    1.69  	if (preferences && !added) {
    1.70  		added = YES;
    1.71 @@ -53,4 +100,30 @@
    1.72  
    1.73  	return preferences;
    1.74  }
    1.75 ++ (id) sharedPreferencesFromAppKitSwizzledByGrowlMail {
    1.76 +	//Stub implementation to swizzle out.
    1.77 +	return nil;
    1.78 +}
    1.79 +
    1.80  @end
    1.81 +
    1.82 +static void GMExchangeMethodImplementations(Method a, Method b)
    1.83 +{
    1.84 +#if __OBJC2__
    1.85 +	method_exchangeImplementations(a, b);
    1.86 +#else
    1.87 +	NSCAssert(a && b, @"Attempt to swizzle fewer than two method implementations");
    1.88 +
    1.89 +	//Adapted from ObjC 1 implementation written by an unknown author and posted to http://www.cocoadev.com/index.pl?MethodSwizzling
    1.90 +	char *tempTypes;
    1.91 +	IMP tempImp;
    1.92 +
    1.93 +	tempTypes = a->method_types;
    1.94 +	a->method_types = b->method_types;
    1.95 +	b->method_types = tempTypes;
    1.96 +
    1.97 +	tempImp = a->method_imp;
    1.98 +	a->method_imp = b->method_imp;
    1.99 +	b->method_imp = tempImp;
   1.100 +#endif
   1.101 +}