Slide 1

Slide 1 text

Practical Runtime Hackery Peter Steinberger @steipete

Slide 2

Slide 2 text

PSPDFKit iOS PDF Framework

Slide 3

Slide 3 text

Complexity

Slide 4

Slide 4 text

Use iOS 7 methods on iOS 5/6 PSPDFModernizer.h

Slide 5

Slide 5 text

#if  __IPHONE_OS_VERSION_MIN_REQUIRED  <  70000   @interface  UIView  (PSPDFModernizer)   +  (void)performWithoutAnimation:(void  (^)(void))action;   @end   #endif

Slide 6

Slide 6 text

#if  __IPHONE_OS_VERSION_MIN_REQUIRED  <  70000   @interface  UIView  (PSPDFModernizer)   +  (void)performWithoutAnimation:(void  (^)(void))action;   @end   #endif

Slide 7

Slide 7 text

IMP  impl  =  imp_implementationWithBlock(^(Class   theClass,  dispatch_block_t  block)  {            if  (block)  {                      [CATransaction  begin];                      [CATransaction  setDisableActions:YES];                      block();                      [CATransaction  commit];            } });

Slide 8

Slide 8 text

IMP  impl  =  imp_implementationWithBlock(^(Class   theClass,  dispatch_block_t  block)  {            if  (block)  {                      [CATransaction  begin];                      [CATransaction  setDisableActions:YES];                      block();                      [CATransaction  commit];            } });

Slide 9

Slide 9 text

__attribute__((constructor))  static  void  PSPDFAddPerformWithoutAnimation(void)  {        SEL  performWithoutAnimationSEL  =  @selector(performWithoutAnimation:);        if  (![UIView  respondsToSelector:performWithoutAnimationSEL])  {                Class  metaClass  =  object_getClass(UIView.class);                class_addMethod(metaClass,  performWithoutAnimationSEL,  impl,  "v@:@?");        } }

Slide 10

Slide 10 text

__attribute__((constructor))  static  void  PSPDFAddPerformWithoutAnimation(void)  {        SEL  performWithoutAnimationSEL  =  @selector(performWithoutAnimation:);        if  (![UIView  respondsToSelector:performWithoutAnimationSEL])  {                Class  metaClass  =  object_getClass(UIView.class);                class_addMethod(metaClass,  performWithoutAnimationSEL,  impl,  "v@:@?");        } }

Slide 11

Slide 11 text

__attribute__((constructor))

Slide 12

Slide 12 text

v@:@?

Slide 13

Slide 13 text

v12@0:4@?8

Slide 14

Slide 14 text

“The only reason why they [the numbers] still exist is for binary compatibility; there are bits of esoteric code here and there that still parse the type encoding string with the expectation that there will be random numbers sprinkled here and there.” - Apple

Slide 15

Slide 15 text

+  (void)performWithoutAnimation:(void  (^)(void))actionsWithoutAnimation  NS_AVAILABLE_IOS(7_0); v@:@?

Slide 16

Slide 16 text

v@:@? void  methImpl_static_UIView_performWithoutAnimation_(id  self,  SEL  selector,  dispatch_block_t  block);

Slide 17

Slide 17 text

v @:@? void  methImpl_static_UIView_performWithoutAnimation_(id  self,  SEL  selector,  dispatch_block_t  block);

Slide 18

Slide 18 text

v @ :@? void  methImpl_static_UIView_performWithoutAnimation_(id  self,  SEL  selector,  dispatch_block_t  block);

Slide 19

Slide 19 text

v @ : @? void  methImpl_static_UIView_performWithoutAnimation_(id  self,  SEL  selector,  dispatch_block_t  block);

Slide 20

Slide 20 text

v @ : @? void  methImpl_static_UIView_performWithoutAnimation_(id  self,  SEL  selector,  dispatch_block_t  block);

Slide 21

Slide 21 text

v44@0:4d8d16f24f28I32@?36@?40 + (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion: (void (^)(BOOL finished))completion NS_AVAILABLE_IOS(7_0);

Slide 22

Slide 22 text

__attribute__((constructor))  static  void  PSPDFKitAddPerformWithoutAnimation(void)  {        @autoreleasepool  {                SEL  performWithoutAnimationSEL  =  @selector(performWithoutAnimation:);                if  (![UIView  respondsToSelector:performWithoutAnimationSEL])  {                        IMP  impl  =  imp_implementationWithBlock(^(Class  theClass,  dispatch_block_t  block)  {                                [CATransaction  begin];                                [CATransaction  setDisableActions:YES];                                if  (block)  block();                                [CATransaction  commit];                        });                        Class  metaClass  =  object_getClass(UIView.class);                        if  (!class_addMethod(metaClass,  performWithoutAnimationSEL,  impl,  "v@:@?"))  {                                NSLog(@"Failed  to  add  performWithoutAnimationSEL:  to  UIView.");                        }                }        } }

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

Fast runtime queries PSPDFAnnotationMapper.h

Slide 25

Slide 25 text

@interface  PSPDFAnnotationMapper  :  NSObject   +  (instancetype)defaultMapper;   -­‐  (Class)annotationClassForType:(PSPDFAnnotationType)type;   @end

Slide 26

Slide 26 text

Class  *objc_copyClassList(unsigned  int  *outCount)  __OSX_AVAILABLE_STARTING(__MAC_10_7,  __IPHONE_3_1);

Slide 27

Slide 27 text

[NSObject  isSubclassOfClass:]

Slide 28

Slide 28 text

[NSObject  isSubclassOfClass:] = bad idea

Slide 29

Slide 29 text

NS_INLINE  BOOL  PSPDFIsSubclassOfClass(Class  subclass,  Class  superclass) {        for  (Class  class  =  class_getSuperclass(subclass);  class  !=  Nil;                  class  =  class_getSuperclass(class))  {                if  (class  ==  superclass)  return  YES;        }        return  NO; }

Slide 30

Slide 30 text

NS_INLINE  BOOL  PSPDFIsSubclassOfClass(Class  subclass,  Class  superclass) {        for  (Class  class  =  class_getSuperclass(subclass);  class  !=  Nil;                  class  =  class_getSuperclass(class))  {                if  (class  ==  superclass)  return  YES;        }        return  NO; }

Slide 31

Slide 31 text

inline is a lie __inline__ __attribute__((always_inline))

Slide 32

Slide 32 text

NS_INLINE  BOOL  PSPDFIsSubclassOfClass(Class  subclass,  Class  superclass) {        for  (Class  class  =  class_getSuperclass(subclass);  class  !=  Nil;                  class  =  class_getSuperclass(class))  {                if  (class  ==  superclass)  return  YES;        }        return  NO; }

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

NSDictionary  *PSPDFBuildMapOfSubclassesForClass(Class  superclass)  {        NSMutableDictionary  *typeToClassMutable  =  [NSMutableDictionary  dictionary];        unsigned  int  count  =  0;        Class  *classList  =  objc_copyClassList(&count);        for  (int  index  =  0;  index  <  count;  ++index)  {                Class  class  =  classList[index];                if  (class  !=  superclass  &&  PSPDFIsSubclassOfClass(class,  superclass))  {                        NSString  *supportedType  =  [class  supportedType];                        typeToClassMutable[supportedType]  =  class;                }        }  free(classList);        return  typeToClassMutable; }

Slide 36

Slide 36 text

More convenient delegate calling PSPDFDelegateProxy.h

Slide 37

Slide 37 text

if  ([self.delegate  respondsToSelector:@selector(annotationStyleController:willStartChangingProperty:)])  {        self.delegate  annotationStyleController:self  willStartChangingProperty:propertyName]; }

Slide 38

Slide 38 text

-Warc-repeated-use-of-weak

Slide 39

Slide 39 text

id  delegate  =  self.delegate; if  ([delegate  respondsToSelector:@selector(annotationStyleController:willStartChangingProperty:)])  {        delegate  annotationStyleController:self  willStartChangingProperty:propertyName]; }

Slide 40

Slide 40 text

BOOL  accepted  =  YES;         id  delegate  =  self.delegate; if  ([delegate  respondsToSelector:@selector(selectionView:shouldStartSelectionAtPoint:)])  {        accepted  =  [delegate  selectionView:self  shouldStartSelectionAtPoint:location]; }

Slide 41

Slide 41 text

[self.delegateProxy  annotationStyleController:self  willStartChangingProperty:propertyName];

Slide 42

Slide 42 text

BOOL  accepted  =  [[(id)self.delegateProxy  copyThatDefaultsToYES]                                  selectionView:self  shouldStartSelectionAtPoint:location];

Slide 43

Slide 43 text

@interface  PSPDFDelegateProxy  :  NSProxy   -­‐  (id)initWithDelegate:(id)delegate  conformingToProtocol:(Protocol  *)protocol                                                                            defaultReturnValue:(NSValue  *)returnValue;   -­‐  (instancetype)copyThatDefaultsTo:(NSValue  *)defaultValue; -­‐  (instancetype)copyThatDefaultsToYES;     @end

Slide 44

Slide 44 text

-­‐  (id)forwardingTargetForSelector:(SEL)aSelector  __OSX_AVAILABLE_STARTING(__MAC_10_5,  __IPHONE_2_0); -­‐  (NSMethodSignature  *)methodSignatureForSelector:(SEL)aSelector; -­‐  (void)forwardInvocation:(NSInvocation  *)invocation Message forwarding 101

Slide 45

Slide 45 text

-­‐  (id)forwardingTargetForSelector:(SEL)aSelector  __OSX_AVAILABLE_STARTING(__MAC_10_5,  __IPHONE_2_0); -­‐  (NSMethodSignature  *)methodSignatureForSelector:(SEL)aSelector; -­‐  (void)forwardInvocation:(NSInvocation  *)invocation Message forwarding 101

Slide 46

Slide 46 text

-­‐  (BOOL)respondsToSelector:(SEL)selector  {        return  [_delegate  respondsToSelector:selector]; }

Slide 47

Slide 47 text

-­‐  (id)forwardingTargetForSelector:(SEL)selector  {        id  delegate  =  _delegate;        return  [delegate  respondsToSelector:selector]  ?  delegate  :  self; }

Slide 48

Slide 48 text

-­‐  (NSMethodSignature  *)methodSignatureForSelector:(SEL)selector  {        NSMethodSignature  *signature  =  [_delegate  methodSignatureForSelector:selector];                return  signature; }

Slide 49

Slide 49 text

-­‐  (NSMethodSignature  *)methodSignatureForSelector:(SEL)selector  {        NSMethodSignature  *signature  =  [_delegate  methodSignatureForSelector:selector];                return  signature; }        //  oh-­‐ooh.        if  (!signature)  {                if  (!_signatures)  _signatures  =  [self  methodSignaturesForProtocol:_protocol];                signature  =  CFDictionaryGetValue(_signatures,  selector);        }

Slide 50

Slide 50 text

-­‐  (NSMethodSignature  *)_searchAllClassesForSignature:(SEL)sel {        int  count  =  objc_getClassList(NULL,  0);        Class  *classes  =  malloc(sizeof(*classes)  *  count);        objc_getClassList(classes,  count);                NSMethodSignature  *sig  =  nil;        for(int  i  =  0;  i  <  count;  i++)        {                Class  c  =  classes[i];                if  (class_getClassMethod(c,  @selector(methodSignatureForSelector:))  &&                        class_getClassMethod(c,  @selector(instanceMethodSignatureForSelector:)))                {                        NSMethodSignature  *thisSig  =  [c  methodSignatureForSelector:  sel];                        if  (!sig)  sig  =  thisSig;                        else  if  (sig  &&  thisSig  &&  ![sig  isEqual:  thisSig])                        {                                sig  =  nil;  break;                        }                                                thisSig  =  [c  instanceMethodSignatureForSelector:  sel];                        if  (!sig)  sig  =  thisSig;                        else  if(sig  &&  thisSig  &&  ![sig  isEqual:  thisSig])                        {                                sig  =  nil;  break;                        }                }        }        free(classes);        return  sig; }

Slide 51

Slide 51 text

No content

Slide 52

Slide 52 text

struct  objc_method_description  *protocol_copyMethodDescriptionList(      Protocol  *p,  BOOL  isRequiredMethod,  BOOL  isInstanceMethod,  unsigned  int  *outCount)

Slide 53

Slide 53 text

struct  objc_method_description  *protocol_copyMethodDescriptionList(      Protocol  *p,  BOOL  isRequiredMethod,  BOOL  isInstanceMethod,  unsigned  int  *outCount) struct  objc_method_description  {      SEL  name;     char  *types; };

Slide 54

Slide 54 text

struct  objc_method_description  *protocol_copyMethodDescriptionList(      Protocol  *p,  BOOL  isRequiredMethod,  BOOL  isInstanceMethod,  unsigned  int  *outCount) struct  objc_method_description  {      SEL  name;     char  *types; }; +  (NSMethodSignature  *)signatureWithObjCTypes:(const  char  *)types;

Slide 55

Slide 55 text

-­‐  (void)methodSignaturesForProtocol:(Protocol  *)protocol  inDictionary:(CFMutableDictionaryRef)cache  {        void  (^enumerateRequiredMethods)(BOOL)  =  ^(BOOL  isRequired)  {                unsigned  int  methodCount;                struct  objc_method_description  *descr  =  protocol_copyMethodDescriptionList(protocol,  isRequired,                                                                                                                                                                      YES,  &methodCount);                for  (NSUInteger  idx  =  0;  idx  <  methodCount;  idx++)  {                        NSMethodSignature  *signature  =  [NSMethodSignature  signatureWithObjCTypes:descr[idx].types];                        CFDictionarySetValue(cache,  descr[idx].name,  (__bridge  const  void  *)(signature));                }                free(descr);        };                enumerateRequiredMethods(NO);        enumerateRequiredMethods(YES); }

Slide 56

Slide 56 text

-­‐  (void)methodSignaturesForProtocol:(Protocol  *)protocol  inDictionary:(CFMutableDictionaryRef)cache  {        void  (^enumerateRequiredMethods)(BOOL)  =  ^(BOOL  isRequired)  {                unsigned  int  methodCount;                struct  objc_method_description  *descr  =  protocol_copyMethodDescriptionList(protocol,  isRequired,                                                                                                                                                                      YES,  &methodCount);                for  (NSUInteger  idx  =  0;  idx  <  methodCount;  idx++)  {                        NSMethodSignature  *signature  =  [NSMethodSignature  signatureWithObjCTypes:descr[idx].types];                        CFDictionarySetValue(cache,  descr[idx].name,  (__bridge  const  void  *)(signature));                }                free(descr);        };                enumerateRequiredMethods(NO);        enumerateRequiredMethods(YES); }        unsigned  int  inheritedProtocolCount;        Protocol  *__unsafe_unretained*  inheritedProtocols  =  protocol_copyProtocolList(protocol,  &inheritedProtocolCount);        for  (NSUInteger  idx  =  0;  idx  <  inheritedProtocolCount;  idx++)  {                [self  methodSignaturesForProtocol:inheritedProtocols[idx]  inDictionary:cache];        }        free(inheritedProtocols);

Slide 57

Slide 57 text

static  CFMutableDictionaryRef  _protocolCache  =  nil; static  OSSpinLock  _lock  =  OS_SPINLOCK_INIT;   -­‐  (CFDictionaryRef)methodSignaturesForProtocol:(Protocol  *)protocol  {        OSSpinLockLock(&_lock);                if  (!_protocolCache)  _protocolCache  =  CFDictionaryCreateMutable(NULL,  0,  NULL,  &kCFTypeDictionaryValueCallBacks);        CFDictionaryRef  signatureCache  =  CFDictionaryGetValue(_protocolCache,  (__bridge  const  void  *)(protocol));          if  (!signatureCache)  {                signatureCache  =  CFDictionaryCreateMutable(NULL,  0,  NULL,  &kCFTypeDictionaryValueCallBacks);                [self  methodSignaturesForProtocol:protocol  inDictionary:(CFMutableDictionaryRef)signatureCache];                CFDictionarySetValue(_protocolCache,  (__bridge  const  void  *)(protocol),  signatureCache);                CFRelease(signatureCache);        }        OSSpinLockUnlock(&_lock);        return  signatureCache; }

Slide 58

Slide 58 text

id  delegate  =  self.delegate; if  ([delegate  respondsToSelector:@selector(annotationStyleController:willStartChangingProperty:)])  {        delegate  annotationStyleController:self  willStartChangingProperty:propertyName]; }

Slide 59

Slide 59 text

[self.delegateProxy  annotationStyleController:self  willStartChangingProperty:propertyName];

Slide 60

Slide 60 text

BOOL  accepted  =  YES;         id  delegate  =  self.delegate; if  ([delegate  respondsToSelector:@selector(selectionView:shouldStartSelectionAtPoint:)])  {        accepted  =  [delegate  selectionView:self  shouldStartSelectionAtPoint:location]; }

Slide 61

Slide 61 text

BOOL  accepted  =  [[(id)self.delegateProxy  copyThatDefaultsToYES]                                  selectionView:self  shouldStartSelectionAtPoint:location];

Slide 62

Slide 62 text

-­‐  (void)forwardInvocation:(NSInvocation  *)invocation  {        if  (_defaultReturnValue)  {                const  char  *methodReturnType  =  invocation.methodSignature.methodReturnType;                if  (strcmp(_defaultReturnValue.objCType,  methodReturnType)  ==  0)  {                        char  buffer[invocation.methodSignature.methodReturnLength];                        [_defaultReturnValue  getValue:buffer];                        [invocation  setReturnValue:&buffer];                }else  {                        PSPDFLogError(@"Unexpected  invocation  method  signature.”);                }        } }

Slide 63

Slide 63 text

No content

Slide 64

Slide 64 text

-­‐  (void)forwardInvocation:(NSInvocation  *)invocation  {        if  (_defaultReturnValue)  {                const  char  *methodReturnType  =  invocation.methodSignature.methodReturnType;                if  (strcmp(_defaultReturnValue.objCType,  methodReturnType)  ==  0  ||                        (strcmp(_defaultReturnValue.objCType,  "c")  ==  0  &&  strcmp(methodReturnType,  "B")  ==  0))  {                        char  buffer[invocation.methodSignature.methodReturnLength];                        [_defaultReturnValue  getValue:buffer];                        [invocation  setReturnValue:&buffer];                }else  {                        PSPDFLogError(@"Unexpected  invocation  method  signature.”);                }        } }

Slide 65

Slide 65 text

https://github.com/steipete/PSTDelegateProxy

Slide 66

Slide 66 text

When dynamic subclassing actually saves you code UIViewController+PSPDFKitAdditions.h

Slide 67

Slide 67 text

extern  NSString  *const  PSPDFPresentOptionRect; extern  NSString  *const  PSPDFPresentOptionPopoverContentSize; extern  NSString  *const  PSPDFPresentOptionAllowedPopoverArrowDirections; extern  NSString  *const  PSPDFPresentOptionModalPresentationStyle; extern  NSString  *const  PSPDFPresentOptionAlwaysModal; extern  NSString  *const  PSPDFPresentOptionAlwaysPopover; extern  NSString  *const  PSPDFPresentOptionPassthroughViews; extern  NSString  *const  PSPDFPresentOptionWillDismissBlock; extern  NSString  *const  PSPDFPresentOptionHalfModalMode; extern  NSString  *const  PSPDFPresentOptionPersistentCloseButtonMode;   -­‐  (id)presentModalOrInPopover:(UIViewController  *)controller            embeddedInNavigationController:(BOOL)embedded                                          withCloseButton:(BOOL)closeButton                                                        animated:(BOOL)animated                                                            sender:(id)sender                                                          options:(NSDictionary  *)options;

Slide 68

Slide 68 text

@interface  PSPDFNavigationController  :  UINavigationController @property  (nonatomic,  copy)  dispatch_block_t  navigationControllerWillDismissAction;

Slide 69

Slide 69 text

-­‐  (void)viewWillDisappear:(BOOL)animated  {        [super  viewWillDisappear:animated];          //  Execute  the  dismiss  action  if  we're  being  dismissed.        if  (self.isBeingDismissed  &&  self.navigationControllerWillDismissAction)  {                self.navigationControllerWillDismissAction();        } }

Slide 70

Slide 70 text

@interface  UIImagePickerController  :  UINavigationController @interface  MFMailComposeViewController  :  UINavigationController @interface  MFMessageComposeViewController  :  UINavigationController @interface  ABPeoplePickerNavigationController  :  UINavigationController

Slide 71

Slide 71 text

-­‐  (void)pspdf_addWillDismissAction:(dispatch_block_t)dismissAction;

Slide 72

Slide 72 text

/**    *  Sets  the  class  of  an  object.  *    *  @param  obj  The  object  to  modify.  *  @param  cls  A  class  object.  *    *  @return  The  previous  value  of  \e  object's  class,  or  \c  Nil  if  \e  object  is  \c  nil.  */ OBJC_EXPORT  Class  object_setClass(id  obj,  Class  cls);

Slide 73

Slide 73 text

IMP  viewWillDismissIMP  =  imp_implementationWithBlock(^(UIViewController  *_self,  BOOL  animated)  {        dispatch_block_t  dismissBlock  =  objc_getAssociatedObject(_self,  &PSPDFVCWillDismissActionKey);                  if  (_self.isBeingDismissed  &&  dismissBlock)  dismissBlock();        }); }

Slide 74

Slide 74 text

... super?

Slide 75

Slide 75 text

... super? void  (*superIMP)(id,  SEL,  BOOL)  =          (void  *)[_self.class.superclass  instanceMethodForSelector:origSEL]; superIMP(_self,  _cmd,  animated);

Slide 76

Slide 76 text

IMP  viewWillDisappearIMP  =  imp_implementationWithBlock(^(UIViewController  *_self,  BOOL  animated)  {        void  (*superIMP)(id,  SEL,  BOOL)  =  (void  *)[_self.class.superclass  instanceMethodForSelector:origSEL];        superIMP(_self,  _cmd,  animated);          dispatch_block_t  dismissBlock  =  objc_getAssociatedObject(_self,  &  PSPDFVCWillDismissActionKey);                  if  (_self.isBeingDismissed  &&  dismissBlock)  dismissBlock();        }); }

Slide 77

Slide 77 text

1. NSString  *subclassName  =  [NSString  stringWithFormat:@"PSPDF_%@",   NSStringFromClass(self.class)]; 2. Class  subclass  =  objc_allocateClassPair(self.class,  subclassName.UTF8String,  0); 3. class_addMethod(subclass,  origSEL,  viewIMP,   method_getTypeEncoding(class_getInstanceMethod(self.class,  origSEL))); 4. objc_registerClassPair(subclass); 5. object_setClass(self,  subclass); 6. objc_setAssociatedObject(self,  &  PSPDFVCWillDismissActionKey,  dismissAction,   OBJC_ASSOCIATION_COPY_NONATOMIC);

Slide 78

Slide 78 text

Edge Cases...

Slide 79

Slide 79 text

Caveat ARC'er: objc_allocateClassPair() cannot be compiled with ARC.

Slide 80

Slide 80 text

Caveat ARC'er: objc_allocateClassPair() cannot be compiled with ARC. -fno-objc-arc

Slide 81

Slide 81 text

Caveat ARC'er: objc_allocateClassPair() cannot be compiled with ARC. -fno-objc-arc Fixed in iOS 6

Slide 82

Slide 82 text

KVO

Slide 83

Slide 83 text

self.class  !=  object_getClass(self);

Slide 84

Slide 84 text

No content

Slide 85

Slide 85 text

NSKVONotifying_

Slide 86

Slide 86 text

BOOL  PSPDFReplaceMethodWithBlock(Class  c,  SEL  origSEL,  SEL  newSEL,  id  block)  {        PSPDFAssert(c  &&  origSEL  &&  newSEL  &&  block);        if  ([c  respondsToSelector:newSEL])  return  YES;          Method  origMethod  =  class_getInstanceMethod(c,  origSEL);        IMP  impl  =  imp_implementationWithBlock(block);        if  (!class_addMethod(c,  newSEL,  impl,  method_getTypeEncoding(origMethod)))  {                PSPDFLogError(@"Failed  to  add  method:  %@  on  %@",  NSStringFromSelector(newSEL),  c);                return  NO;        }else  {                Method  newMethod  =  class_getInstanceMethod(c,  newSEL);                  if  (class_addMethod(c,  origSEL,  method_getImplementation(newMethod),  method_getTypeEncoding(origMethod)))  {                        class_replaceMethod(c,  newSEL,  method_getImplementation(origMethod),  method_getTypeEncoding(newMethod))                }else  {                        method_exchangeImplementations(origMethod,  newMethod);                }        }        return  YES; }

Slide 87

Slide 87 text

-­‐  (void)pspdf_addWillDismissAction:(dispatch_block_t)dismissAction  {        @synchronized(self)  {                //  First  try  to  get  an  already  defined  block  and  add  chain  the  blocks  if  set.                dispatch_block_t  currentDismissBlock  =  objc_getAssociatedObject(self,  &PSPDFViewControllerWillDismissActionKey);                if  (currentDismissBlock)  {                        currentDismissBlock  =  ^{  currentDismissBlock();  dismissAction();  };                        objc_setAssociatedObject(self,  &PSPDFViewControllerWillDismissActionKey,  currentDismissBlock,  OBJC_ASSOCIATION_COPY_NONATOMIC);                }else  {                        SEL  origSEL  =  @selector(viewWillDisappear:);                          //  Prepare  the  new  method.                        id  block  =  ^(UIViewController  *_self,  BOOL  animated)  {                                //  Call  [super  viewWillDisappear:animated]                                void  (*superIMP)(id,  SEL,  BOOL)  =  (void  *)[_self.class.superclass  instanceMethodForSelector:origSEL];                                superIMP(_self,  _cmd,  animated);                                  //  Execute  the  dismiss  action  if  we're  being  dismissed.                                dispatch_block_t  dismissBlock  =  objc_getAssociatedObject(_self,  &PSPDFViewControllerWillDismissActionKey);                                if  (_self.isBeingDismissed  &&  dismissBlock)  dismissBlock();                        };                          //  If  the  class  is  lying  about  their  class,  it's  most  likely  KVO.                        if  (self.class  !=  object_getClass(self))  {                                SEL  newSEL  =  NSSelectorFromString([NSString  stringWithFormat:@"pspdf_%@",  NSStringFromSelector(origSEL)]);                                if  (PSPDFReplaceMethodWithBlock(self.class,  origSEL,  newSEL,  block))  {                                        objc_setAssociatedObject(self,  &PSPDFViewControllerWillDismissActionKey,  dismissAction,  OBJC_ASSOCIATION_COPY_NONATOMIC);                                }                        }else  {                                //  Make  a  dynamic  subclass  so  we  can  hook  onto  viewWillDisappear.                                const  char  *prefix  =  "PSPDF_";                                NSString  *className  =  NSStringFromClass(self.class);                                if  (strncmp(prefix,  className.UTF8String,  strlen(prefix))  ==  0)  {  return;  }  //  Error!                                  NSString  *subclassName  =  [NSString  stringWithFormat:@"%s%@",  prefix,  className];                                Class  subclass  =  NSClassFromString(subclassName);                                  if  (subclass  ==  nil)  {                                        subclass  =  objc_allocateClassPair(self.class,  subclassName.UTF8String,  0);                                        if  (subclass  !=  nil)  {                                                IMP  viewWillDisappearIMP  =  imp_implementationWithBlock(block);                                                class_addMethod(subclass,  origSEL,  viewWillDisappearIMP,  method_getTypeEncoding(class_getInstanceMethod(self.class,  origSEL)));                                                objc_registerClassPair(subclass);                                        }                                }                                if  (subclass)  {                                        object_setClass(self,  subclass);                                        objc_setAssociatedObject(self,  &PSPDFViewControllerWillDismissActionKey,  dismissAction,  OBJC_ASSOCIATION_COPY_NONATOMIC);                                }                        }                }        }

Slide 88

Slide 88 text

No content

Slide 89

Slide 89 text

ReactiveCocoa

Slide 90

Slide 90 text

-­‐  (RACSignal  *)rac_signalForSelector:(SEL)selector;

Slide 91

Slide 91 text

No content

Slide 92

Slide 92 text

PSPDFAnnotationMapper

Slide 93

Slide 93 text

PSPDFModernizer PSPDFAnnotationMapper

Slide 94

Slide 94 text

PSPDFModernizer PSPDFAnnotationMapper PSPDFDelegateProxy

Slide 95

Slide 95 text

PSPDFModernizer PSPDFAnnotationMapper PSPDFDelegateProxy UIViewController+PSPDFKitAdditions

Slide 96

Slide 96 text

Complexity Simplicity

Slide 97

Slide 97 text

Peter Steinberger @steipete