Upload
-
View
1.128
Download
2
Embed Size (px)
DESCRIPTION
Citation preview
Previously on Cocoaheads@Taipei
Zonble's talk (about AOP)http://goo.gl/4Rlbb2
羅馬不是一天造成的Unmaintainable 的 code 也是
1| - (void)appendData:(NSData*)inData{ 2| [_data appendData:inData]; 3| }
Life should be simple, but...
1| - (void)appendData:(NSData*)inData{ 2| NSParameterAssert(inData != nil); 3| NSParameterAssert([inData length]); 4| if (!inData) { 5| NSLog(@"inData is nil!"); 6| return; 7| } 8| if (![inData length]) { 9| return;10| }11| NSInteger length = [_data length];12| [_lock lock];13| [_data appendData:inData];14| [_lock unlock];15| NSParameterAssert(length != [_data length]);16| #if DEBUG17| [TestFlight passCheckpoint:@"APPEND_DATA"];18| #else19| [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| #endif23| }
main taskfor thread safe
for guarding and checking
for monitoring and analyzing
Welcome to real life
You add lots of code for
...and everywhere!
•確定傳入參數•確定有插入資料•加 lock
•加 Debug Log
•加 TestFlight Log、Flurry Log...
XSpectspect: look, see
http://www.prefixsuffix.com
Xaree's Spect library
XSpect 包含了 2 個獨立的套件
•XAspect: 以 Aspect-Oriented 的方式,讓程式碼 reusable 及 maintainable。
•XIntrospect: 把小程式碼包裝成更容易重複使用的 block (使用 Block-in-Block)。
Main Task
subtask
subtask
subtask
subtask
subtask
subtask
subtask
Main Task
subtask
subtask
subtask
advice
advice
advice
advice
Main Task
advice
advice
advice
advice
advice
advice
advice
advice
Main Task
advice
advice
advice
advice
advice
advice
advice
advice
XAspectXIntrospec
Using XSpectTraditionalObj-C
message
method
method
Obj-Cmessage
Obj-Cmessage
Obj-Cmessage
Live Demo
Main Task
subtask
subtask
advice
advice
Main Task
subtask
subtask
advice
advice
Aspect advices:Introspective tasks:
• 程式是由許多 tasks 組成Main task: 為程式的主架構Subtasks 應該要很容易的加入或移除
• 我把 subtask 分為兩類:introspective subtasks aspect subtasks (advices)
From introspection (inner-spect) From aspects (outer-spect)
Inside a method
Depending on the main task
Outside a method
Independent jobs (usually)
aspect categories
reusable and maintainablereusable and maintainable
introspective blocks
Obj-Cmessage
利用 Obj-C runtime 的特性,攔截並重新導向 message,以獲得執
行額外程式碼的機會。
XAspectUsing Method Swizzling
(Aspect-Oriented Programming)
Procedures(C function calls)
Func
Func
Func
Func
Func
Func
Func
Func
Func
Func
Func
Func
Func
Func
Func
Procedure-OrientedEvent A Event B Event C
Procedures(Obj-C messages)
IMP
IMP
IMP
IMP
IMP
Obj-Cmessage
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
Object-OrientedObject-Oriented
Class
IMP1IMP2IMP3
.
Class
IMP1IMP2IMP3
.
Class
IMP1IMP2IMP3
.
Class
IMP1IMP2IMP3
.
Event A Event B Event C
Procedures(Obj-C messages)
IMP
IMP
IMP
IMP
IMP
Obj-Cmessage
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
A1 A2 A3
B1 B2 B3
Event A Event B Event C
Aspect-Oriented
Cross-Cutting concerns
logging
security
Object-Oriented
Class
IMP1IMP2IMP3
.
Class
IMP1IMP2IMP3
.
Class
IMP1IMP2IMP3
.
Class
IMP1IMP2IMP3
.
Aspect-Oriented
Aspect
A1A2A3..
Aspect
B1B2B3..
Class
SEL A
IMP A
SEL B
IMP B
Class
SEL A
IMP A
SEL B
IMP B
Before Swizzling After Swizzling
Method Swizzling
Class
SEL A
IMP A
SEL B
IMP B
Class
SEL A
IMP A
SEL B
IMP B
Before Swizzling After Swizzling
IMP B
SEL B
IMP B
SEL B
XAspectCreate a recursive invocation
before Method Swizzling
1
2
@implementation User- (NSString*)userName{ NSString *userName = @"Xaree Lee"; NSLog(@"I'm %@", userName); return userName;}@end
// In –viewDidLoad or –application:didFinishLaunchingWithOptions:User *user = [User new];NSLog(@"The user is: %@", [user userName]);
Live Demo
2013-10-14 23:04:16.454 XSpect[9199:a0b] I'm Xaree Lee2013-10-14 23:04:16.559 XSpect[9199:a0b] The user is: Xaree Lee
2013-10-14 23:04:16.451 XSpect[9199:a0b] ==> Hello, what's your name?2013-10-14 23:04:16.454 XSpect[9199:a0b] I'm Xaree Lee2013-10-14 23:04:16.558 XSpect[9199:a0b] ==> Greeting, Xaree Lee2013-10-14 23:04:16.559 XSpect[9199:a0b] The user is: Xaree Lee
// 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
Adding Aspect
@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
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
XAspectExtensionstyle
ClassSEL B
IMP B
SEL A
IMP A
SEL B
IMP B IMP C
SEL C
IMP B
SEL B
ClassSEL A
IMP A IMP C
SEL C
IMP B
SEL BSEL B
IMP B
SEL C
IMP C IMP C
SEL CSEL D
IMP D
ClassSEL A
@"A"IMP C
SEL C
IMP B
SEL BSEL B
@"B1"@SEL B@"B2"
SEL C
@"C1"@SEL C@"C2"
IMP C
SEL CSEL D
@"D1"@SEL D@"D2"
> D1> C1> B1> A> B2> C2> D2
More Aspects
Add 2nd Aspect
Add 3rd Aspect
results
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)
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)
XIntrospect
把小片段的程式碼包裝成可重複使用的 Block,然後 runtime 的時候再把它們組合起來,並執行。
Using Block-in-Block technique
Why Call it "Introspect"並非所有 subtasks 都是來自獨立的外部觀點
1| - (void)appendData:(NSData*)inData{ 2| NSParameterAssert(inData != nil); 3| NSParameterAssert([inData length]); 4| if (!inData) { 5| NSLog(@"inData is nil!"); 6| return; 7| } 8| if (![inData length]) { 9| return;10| }11| NSInteger length = [_data length];12| [_lock lock];13| [_data appendData:inData];14| [_lock unlock];15| NSParameterAssert(length != [_data length]);16| #if DEBUG17| [TestFlight passCheckpoint:@"APPEND_DATA"];18| #else19| [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| #endif23| }
main taskfor thread safe
for guarding and checking
for monitoring and analyzing
Low Coupling vs High Coupling subtasks to the main task
high
?
low
在 main task 的前後,總是有一些瑣碎的程式碼是必要的。它們是完整的程式碼中的一部分。
XIntrospect 的要點在於
Block-in-Block
就像是俄羅斯娃娃
•Matryoshka:它包含了小片段的程式碼。並可能會在內部包裝另一個 Matryoshka。在被執行之後,會從最外層開始一層一層執行程式碼。(Matryoshka 必須在 IntrospectBlock 內產生)
•IntrospectBlock:實際產生及包裝 Matryoshka 的
Block。它會決定此層的 Matryoshka 的程式碼,並呼叫更內一層的 Matryoshka。(IntrospectBlock 是實際上決定程式碼的地方)
Core Block Typestypedef void (^Matryoshka)();typedef Matryoshka (^IntrospectBlock)(Matryoshka innerBlock);
/** Declare an IntrospectBlock **/IntrospectionBlock introspect = ^ Matryoshka(Matryoshka innerBlock){ return ^(){ NSLog(@"before advice"); innerBlock(); NSLog(@"after advice"); };};
/** XIntrospectCore Definition **/typedef void (^Matryoshka)();typedef Matryoshka (^IntrospectBlock)(Matryoshka innerBlock);Matryoshka assembleMatryoshka(IntrospectBlock introspection, ... );
Define IntrospectBlock
/** Declare another IntrospectBlock **/IntrospectBlock mainTask = ^ Matryoshka(Matryoshka innerBlock){ return ^(){ NSLog(@"Here's the main task"); };};
/** 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");
/** XIntrospectCore Definition **/typedef void (^Matryoshka)();typedef Matryoshka (^IntrospectBlock)(Matryoshka innerBlock);Matryoshka assembleMatryoshka(IntrospectBlock introspection, ... );
2013-10-15 23:17:09.444 XSpect[10654:a0b] Start to assemble a matryoshka2013-10-15 23:17:09.451 XSpect[10654:a0b] Prepare to invoke matryoshka2013-10-15 23:17:09.452 XSpect[10654:a0b] before advice2013-10-15 23:17:09.452 XSpect[10654:a0b] before advice2013-10-15 23:17:09.453 XSpect[10654:a0b] before advice2013-10-15 23:17:09.453 XSpect[10654:a0b] Here's the main task2013-10-15 23:17:09.454 XSpect[10654:a0b] after advice2013-10-15 23:17:09.454 XSpect[10654:a0b] after advice2013-10-15 23:17:09.455 XSpect[10654:a0b] after advice2013-10-15 23:17:09.456 XSpect[10654:a0b] Did invoke matryoshka
Advantages
•Keep SRP principle(single responsibility principle)
•Intuitive coding style(using the extension macros)
•Reusable and readable(encapsulate all code in a block)
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)
1| - (void)appendData:(NSData*)inData{ 2| NSParameterAssert(inData != nil); 3| NSParameterAssert([inData length]); 4| if (!inData) { 5| NSLog(@"inData is nil!"); 6| return; 7| } 8| if (![inData length]) { 9| return;10| }11| NSInteger length = [_data length];12| [_lock lock];13| [_data appendData:inData];14| [_lock unlock];15| NSParameterAssert(length != [_data length]);16| #if DEBUG17| [TestFlight passCheckpoint:@"APPEND_DATA"];18| #else19| [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| #endif23| }
main taskfor thread safe
for guarding and checking
for monitoring and analyzing
Using XSpect library toKeep those code cleaner
Using XAspect
Using XIntrospect
Using XIntrospect
Using XAspectOr
XSpectGithub: xareelee/XSpectCocoaPods: preparing
Thanks