text: //
  //  NSObject+FMAdd.h
  //  FMCategoryKit <https://github.com/yfming93/FMCategoryKit>
  //
  //  Created by mingo on 14/10/8.
  //  Copyright (c) 2015 mingo.
  //
  //  This source code is licensed under the MIT-style license found in the
  //  LICENSE file in the root directory of this source tree.
  //
  
  #import <Foundation/Foundation.h>
  
  NS_ASSUME_NONNULL_BEGIN
  typedef void (^KVOBlock)(NSDictionary *change, void *context);
  
  /**
   Common tasks for NSObject.
   */
  @interface NSObject (FMAdd)
  
      /// VC通用NSInteger属性
  @property (nonatomic, assign) NSInteger ktype;
      /// VC通用字符串属性
  @property (nonatomic, strong) NSString *kname;
  
  /** 版本号 */
  + (NSString *)version;
  
  /** build号 */
  + (NSInteger)build;
  
  /** Bundle id */
  + (NSString *)identifier;
  
  /** 语言 */
  + (NSString *)currentLanguage;
  
  /** 架构 */
  + (NSString *)deviceModel;
  
  /** 类名 */
  - (NSString *)className;
  
  /** 类名 */
  + (NSString *)className;
  
  /** 父类名 */
  - (NSString *)superClassName;
  
  /** 父类名 */
  + (NSString *)superClassName;
  
  /** 实例属性字典 */
  - (NSDictionary *)propertyDictionary;
  
  /** 属性名称列表 */
  - (NSArray *)propertyKeys;
  
  /** 属性名称列表 */
  + (NSArray *)propertyKeys;
  
  /** 方法列表 */
  - (NSArray *)methodList;
  
  /** 方法列表 */
  + (NSArray *)methodList;
  
  /** 格式化方法列表 */
  - (NSArray *)methodListInfo;
  
  /** 创建并返回一个指向所有已注册类的指针列表 */
  + (NSArray *)registedClassList;
  
  /** 实例变量 */
  + (NSArray *)instanceVariable;
  
  /** 是否包含某个属性 */
  - (BOOL)hasPropertyForKey:(NSString*)key;
  
  /** 是否包含某个实例变量 */
  - (BOOL)hasIvarForKey:(NSString*)key;
  
  - (void)modelWithDictionary:(NSDictionary *)dict __attribute__((deprecated(" ")));
  
  
  #pragma mark - Sending messages with variable parameters
  ///=============================================================================
  /// @name Sending messages with variable parameters
  ///=============================================================================
  
  /**
   Sends a specified message to the receiver and returns the result of the message.
   
   @param sel    A selector identifying the message to send. If the selector is
                 NULL or unrecognized, an NSInvalidArgumentException is raised.
   
   @param ...    Variable parameter list. Parameters type must correspond to the
                 selector's method declaration, or unexpected results may occur.
                 It doesn't support union or struct which is larger than 256 bytes.
   
   @return       An object that is the result of the message.
   
   @discussion   The selector's return value will be wrap as NSNumber or NSValue
                 if the selector's `return type` is not object. It always returns nil
                 if the selector's `return type` is void.
   
   Sample Code:
   
       // no variable args
       [view performSelectorWithArgs:@selector(removeFromSuperView)];
       
       // variable arg is not object
       [view performSelectorWithArgs:@selector(setCenter:), CGPointMake(0, 0)];
       
       // perform and return object
       UIImage *image = [UIImage.class performSelectorWithArgs:@selector(imageWithData:scale:), data, 2.0];
       
       // perform and return wrapped number
       NSNumber *lengthValue = [@"hello" performSelectorWithArgs:@selector(length)];
       NSUInteger length = lengthValue.unsignedIntegerValue;
       
       // perform and return wrapped struct
       NSValue *frameValue = [view performSelectorWithArgs:@selector(frame)];
       CGRect frame = frameValue.CGRectValue;
   */
  - (nullable id)performSelectorWithArgs:(SEL)sel, ...;
  
  /**
   Invokes a method of the receiver on the current thread using the default mode after a delay.
   
   @warning      It can't cancelled by previous request.
   
   @param sel    A selector identifying the message to send. If the selector is
                 NULL or unrecognized, an NSInvalidArgumentException is raised immediately.
   
   @param delay  The minimum time before which the message is sent. Specifying
                 a delay of 0 does not necessarily cause the selector to be
                 performed immediately. The selector is still queued on the
                 thread's run loop and performed as soon as possible.
   
   @param ...    Variable parameter list. Parameters type must correspond to the
                 selector's method declaration, or unexpected results may occur.
                 It doesn't support union or struct which is larger than 256 bytes.
   
   Sample Code:
   
       // no variable args
       [view performSelectorWithArgs:@selector(removeFromSuperView) afterDelay:2.0];
       
       // variable arg is not object
       [view performSelectorWithArgs:@selector(setCenter:), afterDelay:0, CGPointMake(0, 0)];
   */
  - (void)performSelectorWithArgs:(SEL)sel afterDelay:(NSTimeInterval)delay, ...;
  
  /**
   Invokes a method of the receiver on the main thread using the default mode.
   
   @param sel    A selector identifying the message to send. If the selector is
                 NULL or unrecognized, an NSInvalidArgumentException is raised.
   
   @param wait   A Boolean that specifies whether the current thread blocks until
                 after the specified selector is performed on the receiver on the
                 specified thread. Specify YES to block this thread; otherwise,
                 specify NO to have this method return immediately.
   
   @param ...    Variable parameter list. Parameters type must correspond to the
                 selector's method declaration, or unexpected results may occur.
                 It doesn't support union or struct which is larger than 256 bytes.
   
   @return       While @a wait is YES, it returns object that is the result of
                 the message. Otherwise return nil;
   
   @discussion   The selector's return value will be wrap as NSNumber or NSValue
                 if the selector's `return type` is not object. It always returns nil
                 if the selector's `return type` is void, or @a wait is YES.
   
   Sample Code:
   
       // no variable args
       [view performSelectorWithArgsOnMainThread:@selector(removeFromSuperView), waitUntilDone:NO];
       
       // variable arg is not object
       [view performSelectorWithArgsOnMainThread:@selector(setCenter:), waitUntilDone:NO, CGPointMake(0, 0)];
   */
  - (nullable id)performSelectorWithArgsOnMainThread:(SEL)sel waitUntilDone:(BOOL)wait, ...;
  
  /**
   Invokes a method of the receiver on the specified thread using the default mode.
   
   @param sel    A selector identifying the message to send. If the selector is
                 NULL or unrecognized, an NSInvalidArgumentException is raised.
   
   @param thread The thread on which to execute aSelector.
   
   @param wait   A Boolean that specifies whether the current thread blocks until
                 after the specified selector is performed on the receiver on the
                 specified thread. Specify YES to block this thread; otherwise,
                 specify NO to have this method return immediately.
   
   @param ...    Variable parameter list. Parameters type must correspond to the
                 selector's method declaration, or unexpected results may occur.
                 It doesn't support union or struct which is larger than 256 bytes.
   
   @return       While @a wait is YES, it returns object that is the result of
                 the message. Otherwise return nil;
   
   @discussion   The selector's return value will be wrap as NSNumber or NSValue
                 if the selector's `return type` is not object. It always returns nil
                 if the selector's `return type` is void, or @a wait is YES.
   
   Sample Code:
   
       [view performSelectorWithArgs:@selector(removeFromSuperView) onThread:mainThread waitUntilDone:NO];
       
       [array  performSelectorWithArgs:@selector(sortUsingComparator:)
                              onThread:backgroundThread
                         waitUntilDone:NO, ^NSComparisonResult(NSNumber *num1, NSNumber *num2) {
           return [num2 compare:num2];
       }];
   */
  - (nullable id)performSelectorWithArgs:(SEL)sel onThread:(NSThread *)thread waitUntilDone:(BOOL)wait, ...;
  
  /**
   Invokes a method of the receiver on a new background thread.
   
   @param sel    A selector identifying the message to send. If the selector is
                 NULL or unrecognized, an NSInvalidArgumentException is raised.
   
   @param ...    Variable parameter list. Parameters type must correspond to the
                 selector's method declaration, or unexpected results may occur.
                 It doesn't support union or struct which is larger than 256 bytes.
   
   @discussion   This method creates a new thread in your application, putting
                 your application into multithreaded mode if it was not already.
                 The method represented by sel must set up the thread environment
                 just as you would for any other new thread in your program.
   
   Sample Code:
   
       [array  performSelectorWithArgsInBackground:@selector(sortUsingComparator:),
                  ^NSComparisonResult(NSNumber *num1, NSNumber *num2) {
          return [num2 compare:num2];
       }];
   */
  - (void)performSelectorWithArgsInBackground:(SEL)sel, ...;
  
  /**
   Invokes a method of the receiver on the current thread after a delay.
   
   @warning     arc-performSelector-leaks
   
   @param sel   A selector that identifies the method to invoke. The method should
                not have a significant return value and should take no argument.
                If the selector is NULL or unrecognized,
                an NSInvalidArgumentException is raised after the delay.
   
   @param delay The minimum time before which the message is sent. Specifying a
                delay of 0 does not necessarily cause the selector to be performed
                immediately. The selector is still queued on the thread's run loop
                and performed as soon as possible.
   
   @discussion  This method sets up a timer to perform the aSelector message on
                the current thread's run loop. The timer is configured to run in
                the default mode (NSDefaultRunLoopMode). When the timer fires, the
                thread attempts to dequeue the message from the run loop and
                perform the selector. It succeeds if the run loop is running and
                in the default mode; otherwise, the timer waits until the run loop
                is in the default mode.
   */
  - (void)performSelector:(SEL)sel afterDelay:(NSTimeInterval)delay;
  
  
  #pragma mark - Swap method (Swizzling)
  ///=============================================================================
  /// @name Swap method (Swizzling)
  ///=============================================================================
  
  /**
   Swap two instance method's implementation in one class. Dangerous, be careful.
   
   @param originalSel   Selector 1.
   @param newSel        Selector 2.
   @return              YES if swizzling succeed; otherwise, NO.
   */
  + (BOOL)swizzleInstanceMethod:(SEL)originalSel with:(SEL)newSel;
  
  /**
   Swap two class method's implementation in one class. Dangerous, be careful.
   
   @param originalSel   Selector 1.
   @param newSel        Selector 2.
   @return              YES if swizzling succeed; otherwise, NO.
   */
  + (BOOL)swizzleClassMethod:(SEL)originalSel with:(SEL)newSel;
  
  
  #pragma mark - Associate value
  ///=============================================================================
  /// @name Associate value
  ///=============================================================================
  
  /**
   Associate one object to `self`, as if it was a strong property (strong, nonatomic).
   
   @param value   The object to associate.
   @param key     The pointer to get value from `self`.
   */
  - (void)setAssociateValue:(nullable id)value withKey:(void *)key;
  
  /**
   Associate one object to `self`, as if it was a weak property (week, nonatomic).
   
   @param value  The object to associate.
   @param key    The pointer to get value from `self`.
   */
  - (void)setAssociateWeakValue:(nullable id)value withKey:(void *)key;
  
  /**
   Get the associated value from `self`.
   
   @param key The pointer to get value from `self`.
   */
  - (nullable id)getAssociatedValueForKey:(void *)key;
  
  /**
   Remove all associated values.
   */
  - (void)removeAssociatedValues;
  
  
  #pragma mark - Others
  ///=============================================================================
  /// @name Others
  ///=============================================================================
  
  /**
   Returns a copy of the instance with `NSKeyedArchiver` and ``NSKeyedUnarchiver``.
   Returns nil if an error occurs.
   */
  - (nullable id)deepCopy;
  
  /**
   Returns a copy of the instance use archiver and unarchiver.
   Returns nil if an error occurs.
   
   @param archiver   NSKeyedArchiver class or any class inherited.
   @param unarchiver NSKeyedUnarchiver clsas or any class inherited.
   */
  - (nullable id)deepCopyWithArchiver:(Class)archiver unarchiver:(Class)unarchiver;
  
  @end
  
  NS_ASSUME_NONNULL_END
  