36
XSpect Makes code reusable and maintainable 2013/10/17 李岡諭 Xaree Lee (leondemon) [email protected]

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

  • Upload
    -

  • View
    1.128

  • Download
    2

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: XSpect, a lightweight library to make your code reusable and maintainable

XSpectMakes code reusable

and maintainable2013/10/17

李岡諭 Xaree Lee (leondemon)[email protected]

Page 2: XSpect, a lightweight library to make your code reusable and maintainable

Previously on Cocoaheads@Taipei

Zonble's talk (about AOP)http://goo.gl/4Rlbb2

Page 3: XSpect, a lightweight library to make your code reusable and maintainable

羅馬不是一天造成的Unmaintainable 的 code 也是

Page 4: XSpect, a lightweight library to make your code reusable and maintainable

1| - (void)appendData:(NSData*)inData{ 2| [_data appendData:inData]; 3| }

Life should be simple, but...

Page 5: XSpect, a lightweight library to make your code reusable and maintainable

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

Page 6: XSpect, a lightweight library to make your code reusable and maintainable

You add lots of code for

...and everywhere!

•確定傳入參數•確定有插入資料•加 lock

•加 Debug Log

•加 TestFlight Log、Flurry Log...

Page 7: XSpect, a lightweight library to make your code reusable and maintainable

XSpectspect: look, see

http://www.prefixsuffix.com

Xaree's Spect library

Page 8: XSpect, a lightweight library to make your code reusable and maintainable

XSpect 包含了 2 個獨立的套件

•XAspect: 以 Aspect-Oriented 的方式,讓程式碼 reusable 及 maintainable。

•XIntrospect: 把小程式碼包裝成更容易重複使用的 block (使用 Block-in-Block)。

Page 9: XSpect, a lightweight library to make your code reusable and maintainable

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

Page 10: XSpect, a lightweight library to make your code reusable and maintainable

Live Demo

Page 11: XSpect, a lightweight library to make your code reusable and maintainable

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

Page 12: XSpect, a lightweight library to make your code reusable and maintainable

利用 Obj-C runtime 的特性,攔截並重新導向 message,以獲得執

行額外程式碼的機會。

XAspectUsing Method Swizzling

(Aspect-Oriented Programming)

Page 13: XSpect, a lightweight library to make your code reusable and maintainable

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

Page 14: XSpect, a lightweight library to make your code reusable and maintainable

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

Page 15: XSpect, a lightweight library to make your code reusable and maintainable

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..

Page 16: XSpect, a lightweight library to make your code reusable and maintainable

Class

SEL A

IMP A

SEL B

IMP B

Class

SEL A

IMP A

SEL B

IMP B

Before Swizzling After Swizzling

Method Swizzling

Page 17: XSpect, a lightweight library to make your code reusable and maintainable

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

Page 18: XSpect, a lightweight library to make your code reusable and maintainable

@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

Page 19: XSpect, a lightweight library to make your code reusable and maintainable

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

Page 20: XSpect, a lightweight library to make your code reusable and maintainable

@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

Page 21: XSpect, a lightweight library to make your code reusable and maintainable

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

Page 22: XSpect, a lightweight library to make your code reusable and maintainable

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)

Page 23: XSpect, a lightweight library to make your code reusable and maintainable

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)

Page 24: XSpect, a lightweight library to make your code reusable and maintainable

XIntrospect

把小片段的程式碼包裝成可重複使用的 Block,然後 runtime 的時候再把它們組合起來,並執行。

Using Block-in-Block technique

Page 25: XSpect, a lightweight library to make your code reusable and maintainable

Why Call it "Introspect"並非所有 subtasks 都是來自獨立的外部觀點

Page 26: XSpect, a lightweight library to make your code reusable and maintainable

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

Page 27: XSpect, a lightweight library to make your code reusable and maintainable

在 main task 的前後,總是有一些瑣碎的程式碼是必要的。它們是完整的程式碼中的一部分。

XIntrospect 的要點在於

Page 28: XSpect, a lightweight library to make your code reusable and maintainable

Block-in-Block

就像是俄羅斯娃娃

Page 29: XSpect, a lightweight library to make your code reusable and maintainable

•Matryoshka:它包含了小片段的程式碼。並可能會在內部包裝另一個 Matryoshka。在被執行之後,會從最外層開始一層一層執行程式碼。(Matryoshka 必須在 IntrospectBlock 內產生)

•IntrospectBlock:實際產生及包裝 Matryoshka 的

Block。它會決定此層的 Matryoshka 的程式碼,並呼叫更內一層的 Matryoshka。(IntrospectBlock 是實際上決定程式碼的地方)

Core Block Typestypedef void (^Matryoshka)();typedef Matryoshka (^IntrospectBlock)(Matryoshka innerBlock);

Page 30: XSpect, a lightweight library to make your code reusable and maintainable

/** 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"); };};

Page 31: XSpect, a lightweight library to make your code reusable and maintainable

/** 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

Page 32: XSpect, a lightweight library to make your code reusable and maintainable

Advantages

•Keep SRP principle(single responsibility principle)

•Intuitive coding style(using the extension macros)

•Reusable and readable(encapsulate all code in a block)

Page 33: XSpect, a lightweight library to make your code reusable and maintainable

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)

Page 34: XSpect, a lightweight library to make your code reusable and maintainable

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

Page 35: XSpect, a lightweight library to make your code reusable and maintainable

XSpectGithub: xareelee/XSpectCocoaPods: preparing

Page 36: XSpect, a lightweight library to make your code reusable and maintainable

Thanks