//
|
// Aspects.h
|
// Aspects - A delightful, simple library for aspect oriented programming.
|
//
|
// Copyright (c) 2014 Peter Steinberger. Licensed under the MIT license.
|
//
|
|
#import <Foundation/Foundation.h>
|
|
typedef NS_OPTIONS(NSUInteger, AspectOptions) {
|
AspectPositionAfter = 0, /// Called after the original implementation (default)
|
AspectPositionInstead = 1, /// Will replace the original implementation.
|
AspectPositionBefore = 2, /// Called before the original implementation.
|
|
AspectOptionAutomaticRemoval = 1 << 3 /// Will remove the hook after the first execution.
|
};
|
|
/// Opaque Aspect Token that allows to deregister the hook.
|
@protocol AspectToken <NSObject>
|
|
/// Deregisters an aspect.
|
/// @return YES if deregistration is successful, otherwise NO.
|
- (BOOL)remove;
|
|
@end
|
|
/// The AspectInfo protocol is the first parameter of our block syntax.
|
@protocol AspectInfo <NSObject>
|
|
/// The instance that is currently hooked.
|
- (id)instance;
|
|
/// The original invocation of the hooked method.
|
- (NSInvocation *)originalInvocation;
|
|
/// All method arguments, boxed. This is lazily evaluated.
|
- (NSArray *)arguments;
|
|
@end
|
|
/**
|
Aspects uses Objective-C message forwarding to hook into messages. This will create some overhead. Don't add aspects to methods that are called a lot. Aspects is meant for view/controller code that is not called a 1000 times per second.
|
|
Adding aspects returns an opaque token which can be used to deregister again. All calls are thread safe.
|
*/
|
@interface NSObject (Aspects)
|
|
/// Adds a block of code before/instead/after the current `selector` for a specific class.
|
///
|
/// @param block Aspects replicates the type signature of the method being hooked.
|
/// The first parameter will be `id<AspectInfo>`, followed by all parameters of the method.
|
/// These parameters are optional and will be filled to match the block signature.
|
/// You can even use an empty block, or one that simple gets `id<AspectInfo>`.
|
///
|
/// @note Hooking static methods is not supported.
|
/// @return A token which allows to later deregister the aspect.
|
+ (id<AspectToken>)aspect_hookSelector:(SEL)selector
|
withOptions:(AspectOptions)options
|
usingBlock:(id)block
|
error:(NSError **)error;
|
|
/// Adds a block of code before/instead/after the current `selector` for a specific instance.
|
- (id<AspectToken>)aspect_hookSelector:(SEL)selector
|
withOptions:(AspectOptions)options
|
usingBlock:(id)block
|
error:(NSError **)error;
|
|
@end
|
|
|
typedef NS_ENUM(NSUInteger, AspectErrorCode) {
|
AspectErrorSelectorBlacklisted, /// Selectors like release, retain, autorelease are blacklisted.
|
AspectErrorDoesNotRespondToSelector, /// Selector could not be found.
|
AspectErrorSelectorDeallocPosition, /// When hooking dealloc, only AspectPositionBefore is allowed.
|
AspectErrorSelectorAlreadyHookedInClassHierarchy, /// Statically hooking the same method in subclasses is not allowed.
|
AspectErrorFailedToAllocateClassPair, /// The runtime failed creating a class pair.
|
AspectErrorMissingBlockSignature, /// The block misses compile time signature info and can't be called.
|
AspectErrorIncompatibleBlockSignature, /// The block signature does not match the method or is too large.
|
|
AspectErrorRemoveObjectAlreadyDeallocated = 100 /// (for removing) The object hooked is already deallocated.
|
};
|
|
extern NSString *const AspectErrorDomain;
|