If -[NSKeyedUnarchiver unarchiveObjectWithData:] is handed a corrupt archive, it raises an exception AND crashes the program. I find this to be very annoying since, almost always, archives come from files or network sources where corruption is possible. Therefore, I have always protected this invocation in a try/catch block.

As I was about to do this for about the fifth time this year, I decided to use the Method Replacement feature of Leopard to replace this method, once and for all, with one that wouldn't crash. I understand that Method Replacement should not be done casually because any plug-in code and even Cocoa itself will use the replaced method. However, I can't think of any usage where a crash would be required behavior.

It seems to work fine, after 10 minutes of testing. Does anyone see any problem with this?

Sincerely,

Jerry Krinock


#import <Cocoa/Cocoa.h>


/*!
 @brief    Improvements to NSKeyedUnarchiver

 @details  Method +unarchiveObjectWithData: has been replaced so that
instead of raising an exception and crashing if given a corrupt archive,
 it just returns nil.  Also, another method has been added which
 returns the exception.
*/
@interface NSKeyedUnarchiver (CatchExceptions)

/*!
 @brief    Like unarchiveObjectWithData:, except it returns the
 exception by reference.

 @param    exception_p  Pointer which will, upon return, if an
 exception occurred and said pointer is not NULL, point to said
 NSException.
 */
+ (id)unarchiveObjectWithData:(NSData*)data
                  exception_p:(NSException**)exception_p ;

@end

#import "NSKeyedUnarchiver+CatchExceptions.h"
#import <objc/runtime.h>

@implementation NSKeyedUnarchiver (CatchExceptions)

+ (id)unarchiveObjectWithData:(NSData*)data
                  exception_p:(NSException**)exception_p {
    id object = nil ;

    @try {
// Note: Since methods were swapped, this is invoking the original method object = [NSKeyedUnarchiver replacement_unarchiveObjectWithData:data] ;
    }
    @catch (NSException* exception) {
        if (exception_p) {
            *exception_p = exception ;
        }
    }
    @finally{
    }

    return object ;
}

+ (void)load {
// Swap the implementations of +unarchiveObjectWithData: and +replacement_unarchiveObjectWithData:. // When the +unarchiveObjectWithData: message is sent to the NSKeyedUnarchiver class object, // +replacement_unarchiveObjectWithData: will be invoked instead. Conversely, // +replacement_unarchiveObjectWithData: will invoke +unarchiveObjectWithData:. Method originalMethod = class_getClassMethod(self, @selector(unarchiveObjectWithData:)) ; Method replacedMethod = class_getClassMethod(self, @selector(replacement_unarchiveObjectWithData:)) ;
    method_exchangeImplementations(originalMethod, replacedMethod) ;
}

+ (id)replacement_unarchiveObjectWithData:(NSData*)data {
    return [self unarchiveObjectWithData:data
                             exception_p:NULL] ;
}

@end


TEST CODE:

int main (int argc, const char * argv[]) {
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

    // Try to unarchive your bash profile
    // This ain't gonna be able to unarchive
NSData* data = [NSData dataWithContentsOfFile:[NSHomeDirectory() stringByAppendingPathComponent:@".bash_profile"]] ;
    NSLog(@"Your bash profile data is %d bytes.", [data length]) ;

    id whatever ;

    // Try it using the normal method
    whatever = [NSKeyedUnarchiver unarchiveObjectWithData:data] ;
    NSLog(@"1.  unarchived whatever = %@", whatever) ;

    // Try it using the improved method that returns the exception:
    NSException** exception ;
    whatever = [NSKeyedUnarchiver unarchiveObjectWithData:data
                                              exception_p:&exception] ;
    NSLog(@"2.  unarchived whatever = %@", whatever) ;
    NSLog(@"exception = %@", exception) ;

    [pool release] ;
}



_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to