美文网首页
Runtime为分类添加属性和截取代理

Runtime为分类添加属性和截取代理

作者: 傻傻小萝卜 | 来源:发表于2019-10-11 10:14 被阅读0次

项目中常常需要扩展系统的类添加属性和方法,这个已经很是常见如:
(1)添加常见系统类型的属性

// 创建属性

@property (nonatomic, assign) BOOL isShowServerItem;

// 创建关联key

static const void *isShowServerItemKey = @"isShowServerItemKey";

// 关联对象set和get

- (void)setIsShowServerItem:(BOOL)isShowServerItem{       objc_setAssociatedObject(self, isShowServerItemKey, [NSNumber numberWithBool:isShowServerItem], OBJC_ASSOCIATION_ASSIGN);    }

- (BOOL)isShowServerItem{    return [objc_getAssociatedObject(self, isShowServerItemKey) boolValue];;    }

在使用的时候切记,关联的是对象,如果不是需要将其变为对象

(2)添加自定义的View

// 创建属性(CustomerServerItem自定义的View)

@property (nonatomic, strong) CustomerServerItem *serverItem;

// 创建关联的key

static NSString *serverItemKey = @"serverItemKey"; 

// 关联对象set和get

- (void)setServerItem:(CustomerServerItem *)serverItem                                                                                                  {    objc_setAssociatedObject(self, &serverItemKey, serverItem, OBJC_ASSOCIATION_RETAIN);}

- (CustomerServerItem *)serverItem

{

     CustomerServerItem *serverItem =  (CustomerServerItem *)objc_getAssociatedObject(self, &serverItemKey);

      if (serverItem == nil) {                                                                                                                                                    serverItem = [[CustomerServerItem alloc] init];

             objc_setAssociatedObject(self, &serverItemKey, serverItem, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

        }

       return serverItem;

}

在我的项目中,需要在所有的大部分的tableView的上边添加一个滑动指示的View,当这个滑动到特定地方,比如说是一个屏幕的长度就显示出快速滑动到头部的地方,所以需要截取滑动方法,并且在滑动的时候将特定的view添加到tableView所属的控制器上,当滑动到该显示那个view的时候,将其显示出来

(1)截取ScrollView的delegate

+ (void)load {

        static dispatch_once_t onceToken;

         dispatch_once(&onceToken, ^{

             Method originalMethod = class_getInstanceMethod([UIScrollView class], @selector(setDelegate:));

             Method replaceMethod = class_getInstanceMethod([UIScrollView class], @selector(hook_setDelegate:));

             method_exchangeImplementations(originalMethod, replaceMethod);

}

(2)改变代理方法

- (void)hook_setDelegate:(id<UIScrollViewDelegate>)delegate {

        [self hook_setDelegate:delegate];

         if ([self isMemberOfClass:[UITableView class]]) {

            //Hook (scrollViewDidScroll:) 方法

             Hook_Method([delegate class], @selector(scrollViewDidScroll:), [self class], @selector(p_scrollViewDidScroll:), @selector(p_scrollViewDidScroll:));

            // 在这个位置可以将这个View添加上去,你也可以在设置isShowServerItem的时候添加

             if ([delegate isMemberOfClass:[UIViewController class]]) {

                            UIViewController *delegateVC = (UIViewController *)delegate;

                             [delegateVC.view addSubview:self.serverItem];

            }

        } else {

             NSLog(@"不是UIScrollView,不需要hook方法");

        }

}

Hook_method这个方法一会在讲解,这个方法中,截取到了scrollView的代理,首先调用了原方法,执行原来代理方法中的内容,然后在这个的基础上,截取和替换了我们要添加的方法

(3)替换方法的实现

-(void)p_scrollViewDidScroll:(UIScrollView *)scrollView

{

// 调用原代理实现的方法,切记在这个方法中,如果要调用你自己为scrollView添加的View属性,一定要用scorllView点出什么来,不要用self,self为代理(tableView添加到的控制器或者是View)

    [self p_scrollViewDidScroll:scrollView];

     CGFloat offSetY = scrollView.contentOffset.y;                                                                                                               CGFloat alpha =  offSetY/(px_scale(400)- NavBarHeight);                                                                                                alpha = MAX(alpha, 0);                                                                                                                                                        alpha = MIN(alpha, 1);

    if (scrollView.isShowServerItem) {

         if (alpha >= 0.5) {

             scrollView.serverItem.isShowScrollTop = YES;

         }else{

            scrollView.serverItem.isShowScrollTop = NO;

          }

    }

}

这样那,如果想在tableView 上显示那个我们定义的View,只需要这个分类导入,并且将isShowServerItem设为Yes既可

这个里边最关键的就是Hook_Method,代码如下,这个可以直接复制使用

static void Hook_Method(Class originalClass, SEL originalSel, Class replacedClass, SEL replacedSel, SEL noneSel){

         // 原实例方法

         Method originalMethod = class_getInstanceMethod(originalClass, originalSel);

         // 替换的实例方法

        Method replacedMethod = class_getInstanceMethod(replacedClass, replacedSel);

        // 如果没有实现 delegate 方法,则手动动态添加

         if (!originalMethod) {

                Method noneMethod = class_getInstanceMethod(replacedClass, noneSel);

                BOOL addNoneMethod = class_addMethod(originalClass, originalSel,                             method_getImplementation(noneMethod), method_getTypeEncoding(noneMethod));

                 if (addNoneMethod) {

                              NSLog(@"******** 没有实现 (%@) 方法,手动添加成功!!",NSStringFromSelector(originalSel));

                   }

                 return;

                }

            // 向实现 delegate 的类中添加新的方法    // 这里是向 originalClass 的 replaceSel(@selector(p_scrollViewDidEndDecelerating:)) 添加 replaceMethod

             BOOL addMethod = class_addMethod(originalClass, replacedSel,                         method_getImplementation(replacedMethod), method_getTypeEncoding(replacedMethod));

               if (addMethod) {

                        // 添加成功

                         NSLog(@"******** 实现了 (%@) 方法并成功 Hook 为 --> (%@)", NSStringFromSelector(originalSel), NSStringFromSelector(replacedSel));

                        // 重新拿到添加被添加的 method,这里是关键(注意这里 originalClass, 不 replacedClass), 因为替换的方法已经添加到原类中了, 应该交换原类中的两个方法

                         Method newMethod = class_getInstanceMethod(originalClass, replacedSel);

                        // 实现交换

                        method_exchangeImplementations(originalMethod, newMethod);

                  }else{

                    // 添加失败,则说明已经 hook 过该类的 delegate 方法,防止多次交换。

                    NSLog(@"******** 已替换过,避免多次替换 --> (%@)",NSStringFromClass(originalClass));

         }

}

最终我这方案没有被采纳,项目陈旧,我的这个也不能完全的适应那个项目,只能作为学习的一次机会了。

相关文章

网友评论

      本文标题:Runtime为分类添加属性和截取代理

      本文链接:https://www.haomeiwen.com/subject/wjzzpctx.html