Aspects.h 3.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. //
  2. // Aspects.h
  3. // Aspects - A delightful, simple library for aspect oriented programming.
  4. //
  5. // Copyright (c) 2014 Peter Steinberger. Licensed under the MIT license.
  6. //
  7. #import <Foundation/Foundation.h>
  8. typedef NS_OPTIONS(NSUInteger, AspectOptions) {
  9. AspectPositionAfter = 0, /// Called after the original implementation (default)
  10. AspectPositionInstead = 1, /// Will replace the original implementation.
  11. AspectPositionBefore = 2, /// Called before the original implementation.
  12. AspectOptionAutomaticRemoval = 1 << 3 /// Will remove the hook after the first execution.
  13. };
  14. /// Opaque Aspect Token that allows to deregister the hook.
  15. @protocol AspectToken <NSObject>
  16. /// Deregisters an aspect.
  17. /// @return YES if deregistration is successful, otherwise NO.
  18. - (BOOL)remove;
  19. @end
  20. /// The AspectInfo protocol is the first parameter of our block syntax.
  21. @protocol AspectInfo <NSObject>
  22. /// The instance that is currently hooked.
  23. - (id)instance;
  24. /// The original invocation of the hooked method.
  25. - (NSInvocation *)originalInvocation;
  26. /// All method arguments, boxed. This is lazily evaluated.
  27. - (NSArray *)arguments;
  28. @end
  29. /**
  30. 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.
  31. Adding aspects returns an opaque token which can be used to deregister again. All calls are thread safe.
  32. */
  33. @interface NSObject (Aspects)
  34. /// Adds a block of code before/instead/after the current `selector` for a specific class.
  35. ///
  36. /// @param block Aspects replicates the type signature of the method being hooked.
  37. /// The first parameter will be `id<AspectInfo>`, followed by all parameters of the method.
  38. /// These parameters are optional and will be filled to match the block signature.
  39. /// You can even use an empty block, or one that simple gets `id<AspectInfo>`.
  40. ///
  41. /// @note Hooking static methods is not supported.
  42. /// @return A token which allows to later deregister the aspect.
  43. + (id<AspectToken>)aspect_hookSelector:(SEL)selector
  44. withOptions:(AspectOptions)options
  45. usingBlock:(id)block
  46. error:(NSError **)error;
  47. /// Adds a block of code before/instead/after the current `selector` for a specific instance.
  48. - (id<AspectToken>)aspect_hookSelector:(SEL)selector
  49. withOptions:(AspectOptions)options
  50. usingBlock:(id)block
  51. error:(NSError **)error;
  52. @end
  53. typedef NS_ENUM(NSUInteger, AspectErrorCode) {
  54. AspectErrorSelectorBlacklisted, /// Selectors like release, retain, autorelease are blacklisted.
  55. AspectErrorDoesNotRespondToSelector, /// Selector could not be found.
  56. AspectErrorSelectorDeallocPosition, /// When hooking dealloc, only AspectPositionBefore is allowed.
  57. AspectErrorSelectorAlreadyHookedInClassHierarchy, /// Statically hooking the same method in subclasses is not allowed.
  58. AspectErrorFailedToAllocateClassPair, /// The runtime failed creating a class pair.
  59. AspectErrorMissingBlockSignature, /// The block misses compile time signature info and can't be called.
  60. AspectErrorIncompatibleBlockSignature, /// The block signature does not match the method or is too large.
  61. AspectErrorRemoveObjectAlreadyDeallocated = 100 /// (for removing) The object hooked is already deallocated.
  62. };
  63. extern NSString *const AspectErrorDomain;