100行實現的iOS輔助類

jopen 10年前發布 | 21K 次閱讀 IOS iOS開發 移動開發

知乎上看到一個問題《一百行以下有那些給力代碼》,幾乎所有回答都是用100行實現的某個小功能,有趣歸有趣,但對實際工作并沒有什么幫助。翻了翻自己的M80Kit,里面也有些比較有趣且實用的輔助類都是一百行左右,可以拿出來秀下。

M80MulticastDelegate

一個從XMPP框架中抽離出來的類,提供同步的一對多delegate機制。

在很多場景下,往往多個對象會依賴于某個核心對象。使用M80MulticastDelegate就能夠很好地進行解耦:不需要定義過多正式的 protocol或者notification進行通信。巧妙地利用OC的消息轉發機制,當發一個不認識的消息給當前MulticaseDelegate 時,它就會自動進行轉發:遍歷已注冊的delegate node,找到能夠響應當前selector的node并執行。

@interface M80DelegateNode : NSObject
@property (nonatomic,weak)  id  nodeDelegate;

  • (M80DelegateNode *)node:(id)delegate; @end

@implementation M80DelegateNode

  • (M80DelegateNode )node:(id)delegate { M80DelegateNode instance = [[M80DelegateNode alloc] init]; instance.nodeDelegate = delegate; return instance; } @end

@interface M80MulticastDelegate () { NSMutableArray *_delegateNodes; }

@end

@implementation M80MulticastDelegate

  • (id)init { if (self = [super init]) {

      _delegateNodes = [[NSMutableArray alloc] init];
    

    } return self; }

  • (void)dealloc {}

  • (void)addDelegate:(id)delegate { [self removeDelegate:delegate]; M80DelegateNode *node = [M80DelegateNode node:delegate]; [_delegateNodes addObject:node]; }

  • (void)removeDelegate:(id)delegate { NSMutableIndexSet *indexs = [NSMutableIndexSet indexSet]; for (NSUInteger i = 0; i < [_delegateNodes count]; i ++) {

      M80DelegateNode *node = [_delegateNodes objectAtIndex:i];
      if (node.nodeDelegate == delegate)
      {
          [indexs addIndex:i];
      }
    

    }

    if ([indexs count]) {

      [_delegateNodes removeObjectsAtIndexes:indexs];
    

    } }

  • (void)removeAllDelegates { [_delegateNodes removeAllObjects]; }

  • (NSUInteger)count { return [_delegateNodes count]; }

  • (NSUInteger)countForSelector:(SEL)aSelector { NSUInteger count = 0; for (M80DelegateNode *node in _delegateNodes) {

      if ([node.nodeDelegate respondsToSelector:aSelector])
      {
          count++;
      }
    

    } return count; }

  • (BOOL)hasDelegateThatRespondsToSelector:(SEL)aSelector { BOOL hasSelector = NO; for (M80DelegateNode *node in _delegateNodes) {

      if ([node.nodeDelegate respondsToSelector:aSelector])
      {
          hasSelector = YES;
          break;
      }
    

    } return hasSelector; }

  • (NSMethodSignature )methodSignatureForSelector:(SEL)aSelector { for (M80DelegateNode node in _delegateNodes) {

      NSMethodSignature *method = [node.nodeDelegate methodSignatureForSelector:aSelector];
      if (method)
      {
          return method;
      }
    

    } //如果發現沒有可以響應當前方法的Node,就返回一個空方法 //否則會引起崩潰 return [[self class] instanceMethodSignatureForSelector:@selector(doNothing)]; }

  • (void)forwardInvocation:(NSInvocation *)invocation { SEL selector = [invocation selector]; BOOL hasNilDelegate = NO;

    for (M80DelegateNode *node in _delegateNodes) {

      id nodeDelegate = node.nodeDelegate;
    
      if (nodeDelegate == nil)
      {
          hasNilDelegate = YES;
      }
      else if ([nodeDelegate respondsToSelector:selector])
      {
          [invocation invokeWithTarget:nodeDelegate];
      }
    

    }

    if (hasNilDelegate) {

      [self removeDelegate:nil];
    

    } }

  • (void)doesNotRecognizeSelector:(SEL)aSelector {}

  • (void)doNothing {}</pre>

    M80TimerHolder

    一個NSTimer的Wrapper,用于NSTimer的管理。iOS上NSTimer的最大坑是它會retain當前target,這樣就導致了target的延遲釋放甚至無法釋放(repeat為YES的NSTimer和target形成retain-cycle又沒有合理時機進行 invalidate)。

    一種通用的解決方式是用Block來解除retain-cycle,但需要在block中將target設為weak,這是這種方式比較容易出錯的地方。

    而M80TimerHolder則采用稍微有點耦合但更安全的方式:真正的Target(通常是VC)和NSTimer持有 M80TimerHolder,后者通過weak delegate和VC進行通信。對于非repeat的Timer,沒有即使不做任何處理都不會有延遲處理和retain-cycle的問題。而對于 repeat的Timer只需要在VC的dealloc方法調用M80TimerHolder的stopTimer方法即可。就算不調用,形成 retain-cycle的也僅僅是Timer和M80TimerHolder,并不會對APP后續執行造成任何影響,只是多了個空跑的Timer和泄露的M80TimerHolder而已。

    具體實現如下:

    @interface M80TimerHolder ()
    {
      NSTimer *_timer;
      BOOL    _repeats;
    }

  • (void)onTimer: (NSTimer *)timer; @end

@implementation M80TimerHolder

  • (void)dealloc { [self stopTimer]; }

  • (void)startTimer: (NSTimeInterval)seconds

        delegate: (id<M80TimerHolderDelegate>)delegate
         repeats: (BOOL)repeats
    

    { _timerDelegate = delegate; _repeats = repeats; if (_timer) {

      [_timer invalidate];
      _timer = nil;
    

    } _timer = [NSTimer scheduledTimerWithTimeInterval:seconds

                                            target:self
                                          selector:@selector(onTimer:)
                                          userInfo:nil
                                           repeats:repeats];
    

    }

  • (void)stopTimer { [_timer invalidate]; _timer = nil; _timerDelegate = nil; }

  • (void)onTimer: (NSTimer *)timer { if (!_repeats) {

      _timer = nil;
    

    } if (_timerDelegate && [_timerDelegate respondsToSelector:@selector(onM80TimerFired:)]) {

      [_timerDelegate onM80TimerFired:self];
    

    } }

@end</pre>

M80WeakProxy

顧名思義,這是個代理,解決的問題和M80TimeHolder一樣:使得NSTimer不持有target以免形成retain-cycle。原理很簡單:NSTimer持有M80WeakProxy,而M80WeakProxy只持有真正target的虛引用,并通過OC的消息轉發機制調用 target的onTimer方法。(Fork from FLAnimatedImage)

具體實現如下

@interface M80WeakProxy : NSObject

  • (instancetype)weakProxyForObject:(id)object; @end

@interface M80WeakProxy () @property (nonatomic,weak) id target; @end

@implementation M80WeakProxy

  • (instancetype)weakProxyForObject:(id)object { M80WeakProxy *instance = [[M80WeakProxy alloc] init]; instance.target = object; return instance; }

  • (id)forwardingTargetForSelector:(SEL)aSelector { return _target; }

  • (void)forwardInvocation:(NSInvocation )invocation { void nullPointer = NULL; [invocation setReturnValue:&nullPointer]; }

  • (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { return [NSObject instanceMethodSignatureForSelector:@selector(init)]; } @end

而真正調用時候只需要這樣:

M80WeakProxy *weakProxy = [M80WeakProxy weakProxyForObject:self];

self.displayLink = [CADisplayLink displayLinkWithTarget:weakProxy selector:@selector(displayDidRefresh:)];

M80Observer

用Block實現KVO,規避KVO register和unregister沒有配對調用的問題。同樣不超過150行,采用的原理和C++中實現自動鎖之類的方式相近,在類初始化時register KVO,析構時unregister KVO以形成配對的調用。

typedef NS_ENUM(NSUInteger, M80ObserveType) { M80ObserveTypeNone, M80ObserveTypeOldAndNew, M80ObserveTypeChange, }; @interface M80Observer () @property (nonatomic,weak) id observeObject; @property (nonatomic,strong) NSString *keyPath; @property (nonatomic,copy) id block; @property (nonatomic,assign) M80ObserveType type;

@end

@implementation M80Observer

  • (void)dealloc { _block = nil; [_observeObject removeObserver:self
                      forKeyPath:_keyPath];
    
    _observeObject = nil; }
  • (instancetype)initWithObject:(id)object

                     keyPath:(NSString *)keyPath
                       block:(id)block
                     options:(NSKeyValueObservingOptions)options
                        type:(M80ObserveType)type
    

    { if (self = [super init]) {

      _observeObject  = object;
      _keyPath        = keyPath;
      _block          = [block copy];
      _type           = type;
      [object addObserver:self
               forKeyPath:keyPath
                  options:options
                  context:NULL];
    

    } return self; }

  • (instancetype)observer:(id)object

               keyPath:(NSString *)keyPath
                 block:(M80ObserverBlock)block
    

    { return [[M80Observer alloc]initWithObject:object

                                    keyPath:keyPath
                                      block:block
                                    options:0
                                       type:M80ObserveTypeNone];
    

    }

  • (instancetype)observer:(id)object

               keyPath:(NSString *)keyPath
        oldAndNewBlock:(M80ObserverBlockWithOldAndNew)block
    

    { return [[M80Observer alloc]initWithObject:object

                                    keyPath:keyPath
                                      block:block
                                    options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew
                                       type:M80ObserveTypeOldAndNew];
    

    }

  • (instancetype)observer:(id)object keyPath:(NSString *)keyPath

               options:(NSKeyValueObservingOptions)options
           changeBlock:(M80ObserverBlockWithChangeDictionary)block
    

    { return [[M80Observer alloc]initWithObject:object

                                    keyPath:keyPath
                                      block:block
                                    options:options
                                       type:M80ObserveTypeChange];

}

  • (void)observeValueForKeyPath:(NSString *)keyPath
                    ofObject:(id)object
                      change:(NSDictionary *)change
                     context:(void *)context
    
    { switch (_type) {
      case M80ObserveTypeNone:
          if (_block)
          {
              ((M80ObserverBlock)_block)();
          }
          break;
      case M80ObserveTypeOldAndNew:
          if (_block)
          {
              ((M80ObserverBlockWithOldAndNew)_block)(change[NSKeyValueChangeOldKey],change[NSKeyValueChangeNewKey]);
          }
          break;
      case M80ObserveTypeChange:
          if (_block)
          {
              ((M80ObserverBlockWithChangeDictionary)_block)(change);
          }
          break;
      default:
          break;
    
    } } @end</pre>來自:http://xiangwangfeng.com/2014/11/21/100%E8%A1%8C%E5%AE%9E%E7%8E%B0%E7%9A%84iOS%E8%BE%85%E5%8A%A9%E7%B1%BB/
 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!