Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Practical Runtime Hackery

Practical Runtime Hackery

NSSpain 2013 - Logroño.

Join me on a tour that shows unconventional yet elegant real-world solutions to common problems on application design, creative ways in utilizing the iOS runtime, smoothy back-port code for older iOS versions and much more. Knowledge of the runtime will enable you to write less code, work faster and above all - it's a lot of fun!

Without the notes, some context will be lost. It's important to understand that some of the approaches here might not be worth it in smaller projects, but they help to reduce the complexity throughout the project and bundle them into one file - with the result that the parts you work on everyday are simpler, even though the new parts might be much more complex. So overall, it *reduces* the total lines of code, and/or their perceived complexity of the system.

832ece085bfe2c7c5b0ed6be62d7e675?s=128

Peter Steinberger
PRO

September 18, 2013
Tweet

Transcript

  1. Practical Runtime Hackery Peter Steinberger @steipete

  2. PSPDFKit iOS PDF Framework

  3. Complexity

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

  5. #if  __IPHONE_OS_VERSION_MIN_REQUIRED  <  70000   @interface  UIView  (PSPDFModernizer)   +

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

     (void)performWithoutAnimation:(void  (^)(void))action;   @end   #endif
  7. IMP  impl  =  imp_implementationWithBlock(^(Class   theClass,  dispatch_block_t  block)  {  

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

             if  (block)  {                      [CATransaction  begin];                      [CATransaction  setDisableActions:YES];                      block();                      [CATransaction  commit];            } });
  9. __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@:@?");        } }
  10. __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@:@?");        } }
  11. __attribute__((constructor))

  12. v@:@?

  13. v12@0:4@?8

  14. “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
  15. +  (void)performWithoutAnimation:(void  (^)(void))actionsWithoutAnimation  NS_AVAILABLE_IOS(7_0); v@:@?

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

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

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

  19. v @ : @? void  methImpl_static_UIView_performWithoutAnimation_(id  self,  SEL  selector,  dispatch_block_t

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

     block);
  21. 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);
  22. __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.");                        }                }        } }
  23. None
  24. Fast runtime queries PSPDFAnnotationMapper.h

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

      @end
  26. Class  *objc_copyClassList(unsigned  int  *outCount)  __OSX_AVAILABLE_STARTING(__MAC_10_7,  __IPHONE_3_1);

  27. [NSObject  isSubclassOfClass:]

  28. [NSObject  isSubclassOfClass:] = bad idea

  29. 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; }
  30. 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; }
  31. inline is a lie __inline__ __attribute__((always_inline))

  32. 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; }
  33. None
  34. None
  35. 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; }
  36. More convenient delegate calling PSPDFDelegateProxy.h

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

    }
  38. -Warc-repeated-use-of-weak

  39. id<PSPDFAnnotationStyleControllerDelegate>  delegate  =  self.delegate; if  ([delegate  respondsToSelector:@selector(annotationStyleController:willStartChangingProperty:)])  {    

       delegate  annotationStyleController:self  willStartChangingProperty:propertyName]; }
  40. BOOL  accepted  =  YES;         id<PSPDFSelectionViewDelegate>  delegate

     =  self.delegate; if  ([delegate  respondsToSelector:@selector(selectionView:shouldStartSelectionAtPoint:)])  {        accepted  =  [delegate  selectionView:self  shouldStartSelectionAtPoint:location]; }
  41. [self.delegateProxy  annotationStyleController:self  willStartChangingProperty:propertyName];

  42. BOOL  accepted  =  [[(id)self.delegateProxy  copyThatDefaultsToYES]          

                           selectionView:self  shouldStartSelectionAtPoint:location];
  43. @interface  PSPDFDelegateProxy  :  NSProxy   -­‐  (id)initWithDelegate:(id)delegate  conformingToProtocol:(Protocol  *)protocol  

                                                                             defaultReturnValue:(NSValue  *)returnValue;   -­‐  (instancetype)copyThatDefaultsTo:(NSValue  *)defaultValue; -­‐  (instancetype)copyThatDefaultsToYES;     @end
  44. -­‐  (id)forwardingTargetForSelector:(SEL)aSelector  __OSX_AVAILABLE_STARTING(__MAC_10_5,  __IPHONE_2_0); -­‐  (NSMethodSignature  *)methodSignatureForSelector:(SEL)aSelector; -­‐  (void)forwardInvocation:(NSInvocation  *)invocation

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

    Message forwarding 101
  46. -­‐  (BOOL)respondsToSelector:(SEL)selector  {        return  [_delegate  respondsToSelector:selector]; }

  47. -­‐  (id)forwardingTargetForSelector:(SEL)selector  {        id  delegate  =  _delegate;

           return  [delegate  respondsToSelector:selector]  ?  delegate  :  self; }
  48. -­‐  (NSMethodSignature  *)methodSignatureForSelector:(SEL)selector  {        NSMethodSignature  *signature  =

     [_delegate  methodSignatureForSelector:selector];                return  signature; }
  49. -­‐  (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);        }
  50. -­‐  (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; }
  51. None
  52. struct  objc_method_description  *protocol_copyMethodDescriptionList(      Protocol  *p,  BOOL  isRequiredMethod,  BOOL

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

     isInstanceMethod,  unsigned  int  *outCount) struct  objc_method_description  {      SEL  name;     char  *types; };
  54. 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;
  55. -­‐  (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); }
  56. -­‐  (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);
  57. 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; }
  58. id<PSPDFAnnotationStyleControllerDelegate>  delegate  =  self.delegate; if  ([delegate  respondsToSelector:@selector(annotationStyleController:willStartChangingProperty:)])  {    

       delegate  annotationStyleController:self  willStartChangingProperty:propertyName]; }
  59. [self.delegateProxy  annotationStyleController:self  willStartChangingProperty:propertyName];

  60. BOOL  accepted  =  YES;         id<PSPDFSelectionViewDelegate>  delegate

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

                           selectionView:self  shouldStartSelectionAtPoint:location];
  62. -­‐  (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.”);                }        } }
  63. None
  64. -­‐  (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.”);                }        } }
  65. https://github.com/steipete/PSTDelegateProxy

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

  67. 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;
  68. @interface  PSPDFNavigationController  :  UINavigationController @property  (nonatomic,  copy)  dispatch_block_t  navigationControllerWillDismissAction;

  69. -­‐  (void)viewWillDisappear:(BOOL)animated  {        [super  viewWillDisappear:animated];    

         //  Execute  the  dismiss  action  if  we're  being  dismissed.        if  (self.isBeingDismissed  &&  self.navigationControllerWillDismissAction)  {                self.navigationControllerWillDismissAction();        } }
  70. @interface  UIImagePickerController  :  UINavigationController @interface  MFMailComposeViewController  :  UINavigationController @interface  MFMessageComposeViewController

     :  UINavigationController @interface  ABPeoplePickerNavigationController  :  UINavigationController
  71. -­‐  (void)pspdf_addWillDismissAction:(dispatch_block_t)dismissAction;

  72. /**    *  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);
  73. IMP  viewWillDismissIMP  =  imp_implementationWithBlock(^(UIViewController  *_self,  BOOL  animated)  {    

       dispatch_block_t  dismissBlock  =  objc_getAssociatedObject(_self,  &PSPDFVCWillDismissActionKey);                  if  (_self.isBeingDismissed  &&  dismissBlock)  dismissBlock();        }); }
  74. ... super?

  75. ... super? void  (*superIMP)(id,  SEL,  BOOL)  =      

       (void  *)[_self.class.superclass  instanceMethodForSelector:origSEL]; superIMP(_self,  _cmd,  animated);
  76. 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();        }); }
  77. 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);
  78. Edge Cases...

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

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

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

    in iOS 6
  82. KVO

  83. self.class  !=  object_getClass(self);

  84. None
  85. NSKVONotifying_

  86. 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; }
  87. -­‐  (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);                                }                        }                }        }
  88. None
  89. ReactiveCocoa

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

  91. None
  92. PSPDFAnnotationMapper

  93. PSPDFModernizer PSPDFAnnotationMapper

  94. PSPDFModernizer PSPDFAnnotationMapper PSPDFDelegateProxy

  95. PSPDFModernizer PSPDFAnnotationMapper PSPDFDelegateProxy UIViewController+PSPDFKitAdditions

  96. Complexity Simplicity

  97. Peter Steinberger @steipete