XSpect, a lightweight library to make your code reusable and maintainable.

1,159 views
1,053 views

Published on

Published in: Technology, Education
0 Comments
11 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,159
On SlideShare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
23
Comments
0
Likes
11
Embeds 0
No embeds

No notes for slide

XSpect, a lightweight library to make your code reusable and maintainable.

  1. 1. XSpect Makes code reusable and maintainable 2013/10/17 李岡諭 Xaree Lee (leondemon) xareelee@gmail.com
  2. 2. Previously on Cocoaheads@Taipei Zonble's talk (about AOP) http://goo.gl/4Rlbb2
  3. 3. 羅馬不是一天造成的 Unmaintainable 的 code 也是
  4. 4. Life should be simple, but... 1| 2| 3| - (void)appendData:(NSData*)inData{ [_data appendData:inData]; }
  5. 5. Welcome to real life 1| - (void)appendData:(NSData*)inData{ 2| NSParameterAssert(inData != nil); 3| NSParameterAssert([inData length]); 4| if (!inData) { 5| NSLog(@"inData is nil!"); for guarding and checking 6| return; 7| } 8| if (![inData length]) { 9| return; 10| } 11| NSInteger length = [_data length]; 12| [_lock lock]; for thread safe 13| [_data appendData:inData]; main task 14| [_lock unlock]; 15| NSParameterAssert(length != [_data length]); 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| }
  6. 6. You add lots of code for •確定傳入參數 •確定有插入資料 •加 lock •加 Debug Log •加 TestFlight Log、Flurry Log... ...and everywhere!
  7. 7. XSpect Xaree's Spect library spect: look, see http://www.prefixsuffix.com
  8. 8. XSpect 包含了 2 個獨立的套件 • XAspect: 以 Aspect-Oriented 的方式,讓 程式碼 reusable 及 maintainable。 • XIntrospect: 把小程式碼包裝成更容易重複 使用的 block (使用 Block-in-Block)。
  9. 9. advice Traditional Obj-C message Using XSpect Obj-C message advice method advice subtask subtask subtask subtask Main Task subtask subtask XAspect XIntrospec subtask Main Task subtask subtask subtask Obj-C message advice advice method advice advice Obj-C message
  10. 10. Live Demo
  11. 11. advice subtask Main Task subtask advice Obj-C message • 程式是由許多 tasks 組成 Main task: 為程式的主架構 Subtasks 應該要很容易的加入或移除 • 我把 subtask 分為兩類: introspective subtasks aspect subtasks (advices) advice subtask Main Task subtask advice Introspective tasks: Aspect advices: From introspection (inner-spect) From aspects (outer-spect) Inside a method Outside a method Depending on the main task Independent jobs (usually) reusable and maintainable introspective blocks reusable and maintainable aspect categories
  12. 12. XAspect Using Method Swizzling (Aspect-Oriented Programming) 利用 Obj-C runtime 的特性, 截 並重新導向 message,以獲得執 行額外程式碼的機會。
  13. 13. Procedure-Oriented Procedures (C function calls) Event A Event B Event C Func Func Func Func Func Func Func Func Func Func Func Func Func Func Func
  14. 14. Object-Oriented Object-Oriented Class Class Class Class IMP1 IMP2 IMP3 . IMP1 IMP2 IMP3 . IMP1 IMP2 IMP3 . IMP1 IMP2 IMP3 . Procedures (Obj-C messages) Event A Event B Event C IMP IMP IMP IMP IMP IMP IMP IMP IMP IMP IMP IMP IMP IMP IMP Obj-C message
  15. 15. Aspect-Oriented Object-Oriented Class Class Class Class IMP1 IMP2 IMP3 . IMP1 IMP2 IMP3 . IMP1 IMP2 IMP3 . IMP1 IMP2 IMP3 . Procedures (Obj-C messages) Event A Event B Event C IMP IMP IMP IMP Cross-Cutting concerns Aspect-Oriented Aspect B1 B2 B3 . . A1 IMP logging IMP A2 IMP IMP A3 IMP security IMP Aspect A1 A2 A3 . . Obj-C message IMP B1 IMP IMP B2 IMP IMP B3
  16. 16. Method Swizzling Before Swizzling After Swizzling Class Class SEL A SEL B SEL A SEL B IMP A IMP B IMP A IMP B
  17. 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. 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. 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. 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. 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. 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. 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)
  24. 24. XIntrospect Using Block-in-Block technique 把小片段的程式碼包裝成可重複使 用的 Block,然後 runtime 的時候 再把它們組合起來,並執行。
  25. 25. Why Call it "Introspect" 並非所有 subtasks 都 是來自獨立的外部觀點
  26. 26. Low Coupling vs High Coupling subtasks to the main task 1| - (void)appendData:(NSData*)inData{ 2| NSParameterAssert(inData != nil); 3| NSParameterAssert([inData length]); 4| if (!inData) { 5| NSLog(@"inData is nil!"); for guarding and checking 6| return; 7| } 8| if (![inData length]) { 9| return; 10| } 11| NSInteger length = [_data length]; 12| [_lock lock]; for thread safe 13| [_data appendData:inData]; main task 14| [_lock unlock]; 15| NSParameterAssert(length != [_data length]); 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| } high ? low
  27. 27. XIntrospect 的要點在於 在 main task 的前後,總是有一 些瑣碎的程式碼是必要的。它們是 完整的程式碼中的一部分。
  28. 28. Blo ck就像 in是俄 Blo 羅斯 ck 娃娃
  29. 29. Core Block Types typedef void (^Matryoshka)(); typedef Matryoshka (^IntrospectBlock)(Matryoshka innerBlock); • Matryoshka:它包含了小片段的程式碼。並可能會在內 部包裝另一個 Matryoshka。在被執行之後,會從最外 層開始一層一層執行程式碼。 (Matryoshka 必須在 IntrospectBlock 內產生) • IntrospectBlock:實際產生及包裝 Matryoshka 的 Block。它會決定此層的 Matryoshka 的程式碼,並呼叫 更內一層的 Matryoshka。 (IntrospectBlock 是實際上決定程式碼的地方)
  30. 30. Define IntrospectBlock /** XIntrospectCore Definition **/ typedef void (^Matryoshka)(); typedef Matryoshka (^IntrospectBlock)(Matryoshka innerBlock); Matryoshka assembleMatryoshka(IntrospectBlock introspection, ... ); /** Declare an IntrospectBlock **/ IntrospectionBlock introspect = ^ Matryoshka(Matryoshka innerBlock){ return ^(){ NSLog(@"before advice"); innerBlock(); NSLog(@"after advice"); }; }; /** Declare another IntrospectBlock **/ IntrospectBlock mainTask = ^ Matryoshka(Matryoshka innerBlock){ return ^(){ NSLog(@"Here's the main task"); }; };
  31. 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. 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. 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. 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| }
  35. 35. XSpect Github: xareelee/XSpect CocoaPods: preparing
  36. 36. Thanks

×