AspectsViewController*aspectsController=[AspectsViewControllernew];[aspectsControlleraspect_hookSelector:@selector(buttonPressed:)withOptions:0usingBlock:^(idinfo,idsender){NSLog(@"Button was pressed by: %@",sender);}error:NULL];
typedefNS_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.};
staticidaspect_add(idself,SELselector,AspectOptionsoptions,idblock,NSError**error){__blockAspectIdentifier*identifier=nil;AspectsContainer*aspectContainer=aspect_getContainerForObject(self,selector);identifier=[AspectIdentifieridentifierWithSelector:selectorobject:selfoptions:optionsblock:blockerror:error];if(identifier){[aspectContaineraddAspect:identifierwithOptions:options];// Modify the class to allow message interception.aspect_prepareClassAndHookSelector(self,selector,error);}returnidentifier;}
staticvoidaspect_prepareClassAndHookSelector(NSObject*self,SELselector,NSError**error){Classklass=aspect_hookClass(self,error);MethodtargetMethod=class_getInstanceMethod(klass,selector);IMPtargetMethodIMP=method_getImplementation(targetMethod);if(!aspect_isMsgForwardIMP(targetMethodIMP)){// Make a method alias for the existing method implementation, it not already copied.constchar*typeEncoding=method_getTypeEncoding(targetMethod);SELaliasSelector=aspect_aliasForSelector(selector);if(![klassinstancesRespondToSelector:aliasSelector]){__unusedBOOLaddedAlias=class_addMethod(klass,aliasSelector,method_getImplementation(targetMethod),typeEncoding);}// We use forwardInvocation to hook in.class_replaceMethod(klass,selector,aspect_getMsgForwardIMP(self,selector),typeEncoding);}}
// This is the swizzled forwardInvocation: method.staticvoid__ASPECTS_ARE_BEING_CALLED__(__unsafe_unretainedNSObject*self,SELselector,NSInvocation*invocation){SELoriginalSelector=invocation.selector;SELaliasSelector=aspect_aliasForSelector(invocation.selector);invocation.selector=aliasSelector;AspectsContainer*objectContainer=objc_getAssociatedObject(self,aliasSelector);AspectsContainer*classContainer=aspect_getContainerForClass(object_getClass(self),aliasSelector);AspectInfo*info=[[AspectInfoalloc]initWithInstance:selfinvocation:invocation];NSArray*aspectsToRemove=nil;// Before hooks.aspect_invoke(classContainer.beforeAspects,info);aspect_invoke(objectContainer.beforeAspects,info);// Instead hooks.BOOLrespondsToAlias=YES;if(objectContainer.insteadAspects.count||classContainer.insteadAspects.count){aspect_invoke(classContainer.insteadAspects,info);aspect_invoke(objectContainer.insteadAspects,info);}else{Classklass=object_getClass(invocation.target);do{if((respondsToAlias=[klassinstancesRespondToSelector:aliasSelector])){[invocationinvoke];break;}}while(!respondsToAlias&&(klass=class_getSuperclass(klass)));}// After hooks.aspect_invoke(classContainer.afterAspects,info);aspect_invoke(objectContainer.afterAspects,info);}