iOS中的链式编程

作者: 01_Jack | 来源:发表于2017-05-24 19:50 被阅读730次

    前言

    作为iOS开发者,很多人看到这个标题最先想到的可能是Masonry和SnapKit。那么什么是链式编程?为什么有人说Masonry/SnapKit是函数式编程,有人说是链式编程?
    其实,函数式编程和链式编程并不是一个层面上的概念。函数式编程是一种编程范式,而链式编程可以理解为函数式编程的一种体现。

    函数式编程(FP)

    在函数式编程中,函数是“第一等公民”。也就是说,函数和其他数据类型一样,可以作为其他函数的参数、返回值。
    举个简单的例子:

    求:(1+2)*3/4的值

    f1(a, b) = a + b
    f2(c) = c*3
    f3(d) = d/4
    那么,f(x) = f3(f2(f1(1, 2)))

    链式编程

    提到链式编程,最醒目的自然是点语法。在OC中,点语法的应用多数仅限于getter、setter,并没有swift中便捷。

    这里说一种OC中的特殊点语法。我们知道,OC是通过[receiver message]来调用方法的,点语法是一种语法糖,最终会调用到对应属性的getter/setter方法。如果我在某个类中写一个方法,是否可以通过点语法来调用这个方法?

    @interface Test : NSObject
    
    - (NSString *)hello;
    
    @end
    
    点语法-0

    可以看到,并没有报错而是出现警告,意思是没有接收getter方法获取到的值。

    点语法-1

    这样就OK了!

    同理可做进一步验证:

    @interface Test : NSObject
    
    - (NSString *)hello;
    - (void)setHello:(NSString *)hello;
    
    @end
    
    点语法-2

    可见,点语法会找到对应的SEL。利用这个特性同样可以在.m文件中同时实现getter、setter方法,而不用写完属性后再写@synthesize,但是由于没有ivar接收这个变量,所以需要手动关联,比较麻烦。.m文件中不能同时实现getter、setter,终究只是因为没有合成对应的ivar,而不是不能同时写getter、setter方法。

    举个例子:

    @interface Test : NSObject
    
    @property (nonatomic, strong) NSString *a;
    
    @end
    
    点语法-3

    并没有出现什么恶心的爆红。又或者像利用runtime给分类添加属性,同样是在没有写@synthesize的情况下仍然可以同时实现setter、getter,终其原因是没用到对应的ivar。

    拉回主战场,有点小跑题。。

    如何实现链式编程?只要在返回值上做手脚就可以了。

    
    @interface Test : NSObject
    
    - (Test *)a;
    - (Test *)b;
    - (Test *)c;
    
    @end
    
    链式语法-0

    这样写的确是连起来了,但是好像不能传参,怎么实现参数的传递?
    回归函数式编程,函数是第一等公民的概念,当返回值是个带参block的getter方法就可以实现参数的传递了。

    @interface Test : NSObject
    
    - (Test *(^)(NSString *str))blk0;
    - (Test *(^)(NSString *str))blk1;
    - (Test *(^)(NSString *str))blk2;
    
    @end
    
    链式语法-1

    来回顾一下思考过程:调用方法-->如何将方法通过点语法调用-->手写getter方法-->实现点语法的链式调用

    到此为止,会发现其实还是通过getter方法来实现各方法之间的链式调用。既然这样,链式语法的调用可以直接通过属性来实现。

    @interface Test : NSObject
    
    @property (nonatomic,  readonly) Test *a;
    @property (nonatomic,  readonly) Test *(^blk)(NSString *str);
    
    @end
    
    
    链式语法-2
    注意:上文说的特殊点语法会找到对应的SEL,并没有提到方法签名。因此,还可以这样写
    @interface Base : NSObject
    
    - (Base *(^)(NSString *))info;
    
    @end
    
    =======================
    
    @implementation Base
    
    - (Base *(^)(NSString *))info {
        return ^(NSString *info){
            self.info = info;
            return self;
        };
    }
    
    - (void)setInfo:(NSString *)info {
         @throw [NSException exceptionWithName:NSInternalInconsistencyException
        reason:[NSString stringWithFormat:@"必须在子类中重写%@方法", NSStringFromSelector(_cmd)] userInfo:nil];
    }
    
    @end
    

    这样的getter、setter看起来很奇怪,因为是手写而不是利用属性自动生成,而点语法只找SEL不找方法签名,因此完全可以改写。

    这样做可以利用getter完成setter赋值,既处理了逻辑关系,又能通过getter完成链式编程。子类的setter怎么实现视具体的需求而定,可以在不同的子类中完成不同的业务逻辑,用起来还是挺方便的。

    现在已经可以实现链式编程了,来试试身手吧!

    小试牛刀

    举个简单的例子,用链式编程撸一遍tableview,这里抛砖引玉只实现简单的数据源方法,感兴趣的童鞋顺着思路继续写。

    Talk is cheap, show me the code.

    @interface UITableView (JKAdd)
    @property (nonatomic, strong) JKTableViewHelper *helper;
    - (void)makeConfigure:(void (^)(JKTableViewHelper *helper))tb;
    @end
    
    @implementation UITableView (JKAdd)
    - (void)setHelper:(JKTableViewHelper *)helper {
        objc_setAssociatedObject(self, @selector(helper), helper, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    - (JKTableViewHelper *)helper {
        return objc_getAssociatedObject(self, @selector(helper));
    }
    - (void)makeConfigure:(void (^)(JKTableViewHelper *))tb {
        JKTableViewHelper *helper = [JKTableViewHelper new];
        !tb ? : tb(helper);
        self.helper = helper;    
    }
    @end
    
    @interface JKTableViewHelper : NSObject <UITableViewDataSource>
    - (JKTableViewHelper *(^)(UITableView *, Class))bindTb;
    - (JKTableViewHelper *(^)(NSInteger))totalSection;
    - (JKTableViewHelper *(^)(NSInteger))section;
    - (JKTableViewHelper *(^)(NSInteger))row;
    - (JKTableViewHelper *(^)(NSArray *))configureCell;
    @end
    
    @interface JKTableViewHelper ()
    @property (nonatomic, weak) UITableView *tableView;
    @property (nonatomic, strong) Class Cls;
    @property (nonatomic, assign) NSInteger sections;
    @property (nonatomic, assign) NSInteger currentSection;
    @property (nonatomic, strong) NSMutableArray *sectionRows;
    @property (nonatomic, strong) NSArray *models;
    @end
    
    
    @implementation JKTableViewHelper
    
    - (JKTableViewHelper *(^)(UITableView *, Class))bindTb {
        return ^(UITableView *tableView, Class Cls){
            tableView.dataSource = self;
            self.tableView = tableView;
            self.Cls = Cls;
            NSCAssert([Cls isSubclassOfClass:[UITableViewCell class]], @"%@必须是UITableViewCell或者它的子类", Cls);
            [tableView registerClass:Cls forCellReuseIdentifier:NSStringFromClass(Cls)] ;
        
            return self;
        };
    
    }
    
    
    - (NSMutableArray *)sectionRows {
        if (_sectionRows == nil) {
            _sectionRows = @[].mutableCopy;
        }
        return _sectionRows;
    }
    
    - (JKTableViewHelper *(^)(NSInteger))totalSection {
        return ^(NSInteger sections){
            self.sections = sections;
            return self;
        };
    }
    
    - (JKTableViewHelper *(^)(NSInteger))section {
        return ^(NSInteger section){
            NSCAssert(section <= self.sections-1, @"section越界");
            self.currentSection = section;
            return self;
        };
    }
    
    - (JKTableViewHelper *(^)(NSInteger))row {
        return ^(NSInteger rows){
            [self.sectionRows insertObject:[NSNumber numberWithInteger:rows] atIndex:self.currentSection];
            return self;
        };
    }
    
    - (JKTableViewHelper *(^)(NSArray *))configureCell {
        return ^(NSArray *models) {
            self.models = models;
            return self;
        };
    }
    
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
        return self.sections;
    }
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
        
        NSInteger i = 0;
        for (NSNumber *num in self.sectionRows) {
            if (section == i) {
                return num.integerValue;
            }
            i++;
        }
        return 0;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass(self.Cls) forIndexPath:indexPath];
        cell.textLabel.text = self.models[indexPath.row];
        return cell;
    }
    
    - (void)dealloc {
        NSLog(@"==%@", NSStringFromSelector(_cmd));
    }
    
    @end
    
    @implementation JKViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
       
        UITableView *tableView = [[UITableView alloc] initWithFrame:self.view.bounds];
        [tableView makeConfigure:^(JKTableViewHelper *helper) {
    
            helper.bindTb(tableView, [UITableViewCell class]).totalSection(1).section(0).row(10).configureCell(@[@"0", @"1", @"2", @"3", @"4", @"5", @"6", @"7", @"8", @"9"]);
            
        }];
        [self.view addSubview:tableView];
    }
    
    @end
    

    其实swift中的链式编程要容易实现的多,毕竟可以放肆的点起来。

    相关文章

      网友评论

      • 郑明明:还不错
      • 春泥Fu:🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾

      本文标题:iOS中的链式编程

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