Created
March 4, 2019 19:49
-
-
Save iamleeg/aabdbec6afaae218c8f4ce93a56cf192 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
NS_ASSUME_NONNULL_BEGIN | |
@interface ContractEnforcer<T> : NSProxy | |
- (T)initWithTarget:(T)target; | |
+ (T)enforcerWithTarget:(T)target; | |
@end | |
NS_ASSUME_NONNULL_END |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "ContractEnforcer.h" | |
@interface NSObject (Contract) | |
- (BOOL)invariant; | |
@end | |
@implementation ContractEnforcer | |
{ | |
id _receiver; | |
} | |
- (id)initWithTarget:(id)target | |
{ | |
_receiver = target; | |
if ([_receiver respondsToSelector:@selector(invariant)]) { | |
NSAssert([_receiver invariant], @"Expect the invariant to initially hold"); | |
} | |
return self; | |
} | |
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel | |
{ | |
return [_receiver methodSignatureForSelector:sel]; | |
} | |
- (void)forwardInvocation:(NSInvocation *)invocation | |
{ | |
// check precondition | |
NSString *methodName = NSStringFromSelector([invocation selector]); | |
NSString *preMethodName = [NSString stringWithFormat:@"pre_%@", methodName]; | |
SEL preMethod = NSSelectorFromString(preMethodName); | |
if ([_receiver respondsToSelector:preMethod]) { | |
NSMethodSignature *preSignature = [_receiver methodSignatureForSelector:preMethod]; | |
NSInvocation *invokePrecondition = [NSInvocation invocationWithMethodSignature:preSignature]; | |
for (int i = 2; i < [preSignature numberOfArguments]; i++) { | |
void *argument = NULL; | |
[invocation getArgument:&argument atIndex:i]; | |
[invokePrecondition setArgument:&argument atIndex:i]; | |
} | |
[invokePrecondition setSelector:preMethod]; | |
[invokePrecondition invokeWithTarget:_receiver]; | |
BOOL satisfied = NO; | |
[invokePrecondition getReturnValue:&satisfied]; | |
NSAssert(satisfied, @"precondition failure invoking [%@ %@]", _receiver, methodName); | |
} | |
// now do the method | |
[invocation invokeWithTarget:_receiver]; | |
// check the postcondition | |
// check the invariant | |
} | |
+ enforcerWithTarget:target | |
{ | |
return [[self alloc] initWithTarget:target]; | |
} | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
// assume the existence of a bridging header | |
@objcMembers | |
class Foo : NSObject | |
{ | |
func doAThing() {} | |
func pre_doAThing() -> Bool { | |
return false; | |
} | |
} | |
let foo : Foo = Foo() | |
let enforcingFoo:Foo = ContractEnforcer<Foo>.enforcer(withTarget: foo) // this typechecks! | |
enforcingFoo.doAThing() // Thread 1: EXC_BAD_ACCESS (code=1, address = 0x300000003) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment