17. XAspect
Create a recursive invocation
before Method Swizzling
Before Swizzling
After Swizzling
2
Class
SEL A
Class
SEL B
SEL A
SEL B
IMP A
IMP B
1
IMP A
IMP B
18. Live Demo
// In –viewDidLoad or –application:didFinishLaunchingWithOptions:
User *user = [User new];
NSLog(@"The user is: %@", [user userName]);
@implementation User
- (NSString*)userName{
NSString *userName = @"Xaree Lee";
NSLog(@"I'm %@", userName);
return userName;
}
@end
2013-10-14 23:04:16.454 XSpect[9199:a0b] I'm Xaree Lee
2013-10-14 23:04:16.559 XSpect[9199:a0b] The user is: Xaree Lee
19. Adding Aspect
// In User+Greeting.m, the Greeting category of User
@implementation User (Greeting)
+ (void)load{
SwapInstanceMethod([self class], @selector(userName),
@selector(Greeting_userName));
}
- (NSString *) Greeting_userName{
// Add before advice here
NSLog(@"==> Hello, what's your name?");
// Invoke recursively
NSString *userName = [self Greeting_userName];
// After advice
NSLog(@"==> Greeting, %@", userName);
return userName;
}
@end
2013-10-14
2013-10-14
2013-10-14
2013-10-14
23:04:16.451
23:04:16.454
23:04:16.558
23:04:16.559
XSpect[9199:a0b]
XSpect[9199:a0b]
XSpect[9199:a0b]
XSpect[9199:a0b]
==>
I'm
==>
The
Hello, what's your name?
Xaree Lee
Greeting, Xaree Lee
user is: Xaree Lee
20. @implementation User (Greeting)
+ (void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SwapInstanceMethod([self class], @selector(userName),
@selector(Greeting_userName));
});
}
- (NSString *) Greeting_userName{
// Add before advice here
NSLog(@"==> Hello, what's your name?");
// Invoke recursively
NSString *userName = [self Greeting_userName];
// After advice
NSLog(@"==> Greeting, %@", userName);
return userName;
}
@end
#undef AspectName
#define AspectName Greeting
XAs p e c t
E x te n s i o
n
s t y le
AspectClass(User)
WeaveAspectInstanceMethods(@selector(userName));
AspectImplementation
- (NSString *) Aspect(userName){
// Add before advice here
NSLog(@"==> Hello, what's your name?");
// Invoke recursively
NSString *userName = [self Aspect(userName)];
// After advice
NSLog(@"==> Greeting, %@", userName);
return userName;
}
EndAspect
21. Class
More
Aspects
SEL A
SEL B
SEL C
IMP A
IMP B
IMP C
IMP B
SEL A
SEL B
SEL C
SEL D
C
IMP A
Add 2nd
Aspect
SEL B
IMP B
IMP C
IMP D
C
Class
Add 3rd
Aspect
Class
results
SEL A
SEL B
SEL C
SEL D
C
@"A"
IMP B
@"B1"
@SEL B
@"B2"
IMP C
@"C1"
@SEL C
@"C2"
IMP C
@"D1"
@SEL D
@"D2"
>
>
>
>
>
>
>
D1
C1
B1
A
B2
C2
D2
22. Advantages
• Keep OCP principle
(open for extension; closed for modification)
• Encapsulate changes
(write all changes in a aspect file)
• Reusable and maintainable
(you can find all the code in one place)
23. Disadvantages
• Hard to understand
(if you aren't familiar with AOP)
• Hard to debug
(I might add some code to deal with it)
• Unpredictable loading sequence
(you should use XAspect for the independent
purpose)
31. /** XIntrospectCore Definition **/
typedef void (^Matryoshka)();
typedef Matryoshka (^IntrospectBlock)(Matryoshka innerBlock);
Matryoshka assembleMatryoshka(IntrospectBlock introspection, ... );
/** Assemble and invoke the whole matryoshka **/
NSLog(@"Start to assemble a matryoshka");
Matryoshka matryoshka = assembleMatryoshka(introspect,
introspect,
introspect,
mainTask,
nil);
NSLog(@"Prepare to invoke matryoshka");
matryoshka();
NSLog(@"Did invoke matryoshka");
2013-10-15
2013-10-15
2013-10-15
2013-10-15
2013-10-15
2013-10-15
2013-10-15
2013-10-15
2013-10-15
2013-10-15
23:17:09.444
23:17:09.451
23:17:09.452
23:17:09.452
23:17:09.453
23:17:09.453
23:17:09.454
23:17:09.454
23:17:09.455
23:17:09.456
XSpect[10654:a0b]
XSpect[10654:a0b]
XSpect[10654:a0b]
XSpect[10654:a0b]
XSpect[10654:a0b]
XSpect[10654:a0b]
XSpect[10654:a0b]
XSpect[10654:a0b]
XSpect[10654:a0b]
XSpect[10654:a0b]
Start to assemble a matryoshka
Prepare to invoke matryoshka
before advice
before advice
before advice
Here's the main task
after advice
after advice
after advice
Did invoke matryoshka
32. Advantages
• Keep SRP principle
(single responsibility principle)
• Intuitive coding style
(using the extension macros)
• Reusable and readable
(encapsulate all code in a block)
33. Disadvantages
• Hard to understand
(if you aren't familiar with Block-in-Block)
• Hard to debug
(I might add some code to deal with it)
• Unpredictable loading sequence
(you should use XAspect for the independent
purpose)
34. Using XSpect library to
Keep those code cleaner
1| - (void)appendData:(NSData*)inData{
2|
NSParameterAssert(inData != nil);
3|
NSParameterAssert([inData length]);
4|
if (!inData) {
Using XIntrospect
5|
NSLog(@"inData is nil!");
for guarding and checking
6|
return;
7|
}
8|
if (![inData length]) {
9|
return;
Using XIntrospect
10|
}
Or
11|
NSInteger length = [_data length];
Using XAspect
12|
[_lock lock];
for thread safe
13|
[_data appendData:inData];
main task
14|
[_lock unlock];
15|
NSParameterAssert(length != [_data length]);
Using XAspect
16| #if DEBUG
for monitoring and analyzing
17|
[TestFlight passCheckpoint:@"APPEND_DATA"];
18| #else
19|
[Flurry logEvent:@"APPEND_DATA"];
20|
id<GAITracker> tracker = [[GAI sharedInstance] defaultTracker];
21|
[tracker send:[[GAIDictionaryBuilder createEventWithCategory:@"data"
withAction:@"append" withLabel:@"user_data" withValue:nil] build]];
22| #endif
23| }