美文网首页
理解Masnory—Part1

理解Masnory—Part1

作者: 关灯侠 | 来源:发表于2016-12-03 23:10 被阅读146次

    Masnory做了什么?

    简单说,只是简化了代码实现Autolayout的方式,本质还是系统API实现的(后面会讲到)。

    创建一个view,左边距离self.view左边50。简单对比一下:

    • 系统API:
     NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:view
                                                                    attribute:NSLayoutAttributeLeft
                                                                    relatedBy:NSLayoutRelationEqual
                                                                       toItem:self.view
                                                                    attribute:NSLayoutAttributeLeft
                                                                   multiplier:1
                                                                     constant:50];
     [self.view addConstraint:constraint];
    
    • Masnory:
    [view mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(self.view).offset(50);
        }];
    

    简单看来,系统API更繁琐,Masnory更简洁,当创建很多约束时这个特点就尤为突出,那么Masnory是怎么做到既简洁又好用的呢?

    Masnory怎么做到的?

    首先得说说里面几个关键类:

    • MASViewConstraint
                                                            / (id) item (or (UIView *) view)
                      (MASViewAttribute *) firstViewAttribute
                     /                                      \(NSLayoutAttribute) layoutAttribute
                      (MASViewAttribute *) secondViewAttribute ...
                     /
    MASViewConstraint 
                     \
                      (NSLayoutRelation) layoutRelation
                     \
                      (CGFloat) layoutMultiplier
                     \
                      (CGFloat) layoutConstant
                     ...
    

    这个类它是Masnory的核心,为什么这样说?看看它的其中一个方法-install就知道,其中有一段实现特别关键:

    MASLayoutConstraint *layoutConstraint = [MASLayoutConstraint constraintWithItem:self.firstViewAttribute.item
                                                                          attribute:self.firstViewAttribute.layoutAttribute
                                                                          relatedBy:self.layoutRelation
                                                                             toItem:self.secondViewAttribute.item
                                                                          attribute:self.secondViewAttribute.layoutAttribute
                                                                         multiplier:self.layoutMultiplier
                                                                           constant:self.layoutConstant];
    

    是不是和NSLayoutConstraint的创建很相似,因为MASLayoutConstraint本就是继承自NSLayoutConstraint,此处的self是指MASViewConstraint。你猜对了,Masnory在用MASViewConstraint的属性值创建一个NSLayoutConstraint对象,里面的所有类都会围绕MASViewConstraint做文章(后面会一一分析),所以也印证了开头总结的,Masnory本质还是使用了系统的API在做约束。

    • MASConstraintMaker
      这是服务MASViewConstraint很重要的一个类,更准确说是服务MASConstraint,因为MASViewConstraint继承自MASConstraint。它是MASConstraint的工厂类,里面有一个工厂方法会频繁用到,先提在这儿,后面也会重点介绍。
     - (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
        MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
        MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
        if ([constraint isKindOfClass:MASViewConstraint.class]) {
            //replace with composite constraint
            NSArray *children = @[constraint, newConstraint];
            MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
            compositeConstraint.delegate = self;
            [self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
            return compositeConstraint;
        }
        if (!constraint) {
            newConstraint.delegate = self;
            [self.constraints addObject:newConstraint];
        }
        return newConstraint;
    }
    
    • MASCompositeConstraint
      这也是继承自MASConstraint,它有个很重要的功能,单次执行超过1个约束以上,就会用到它,它有一个数组childConstraints专门保存每个约束对应的MASViewConstraint对象。(PS:什么叫单次执行,这是我的理解,例如:make.left.top.equalTo(self.view).offset(60); 这个单次执行就会有两个约束left、top,这时就会产生一个MASCompositeConstraint对象。)

    几个重点类和概念心里先有个谱,现在才正式跟踪代码,看具体怎么实现。

    Part1 只围绕一个简单的写法来跟代码,先走通一个方式,后续再添加其他情况。

    例子很简单,在self.view上添加imageView 距离上下左右都是60

        UIImageView *imageView = [[UIImageView alloc]init];
        [self.view addSubview:imageView];
        
        @WeakObj(self);
        [imageView mas_makeConstraints:^(MASConstraintMaker *make) {
            @StrongObj(self);
            make.left.top.equalTo(self.view).offset(60);
            make.right.bottom.equalTo(self.view).offset(-60);
        }];
    

    1、链式语法:
    如果你对它很熟悉,可以略过。
    链式语法我是这样理解的:
    我把里面的涉及到方法,left、top、equalTo、offset都看作同一个类型的属性,然后用get方法串起来的,说起来有点抽象,不过还原一下就知道了。这里拿equalTo举例:

    @property(copy, nonatomic) MASConstraint *(^equalTo)(id);
    - (MASConstraint *(^)(id))equalTo{
    
    }
    

    2、分析工厂方法
    首先会进入到mas_makeConstraints方法,这是view的分类方法,所以imageView才能访问这个方法。这个方法最主要的功能是创建了一个maker传递给block

    - (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
        //关闭自动布局
        self.translatesAutoresizingMaskIntoConstraints = NO;
        //创建maker
        MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
        block(constraintMaker);
        //添加约束
        return [constraintMaker install];
    }
    

    block依次执行left、top、equalTo、offset,这里我看做有两个约束,left、top

    第一个约束总是maker去执行,而且每个约束最终都会走到MASConstraintMaker的一个方法,一个创建MASConstraint子类的工厂方法FUNC1,重点分析(后面都叫FUNC1):

    - (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
        //利用枚举值NSLayoutAttribute、self.view创建的MASViewAttribute
        //其实也只是作为MASViewAttribute的两个属性值保存起来而已
        MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
        
        //利用viewAttribute创建MASViewConstraint,其中只保存了FirstViewAttribute的值。
        //FirstViewAttribute就是需要添加约束的一方的所有属性,SecondViewAttribute会在equalTo后得到
        MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
        
        if ([constraint isKindOfClass:MASViewConstraint.class]) {
            //replace with composite constraint
            NSArray *children = @[constraint, newConstraint];
            MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
            compositeConstraint.delegate = self;
            [self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
            return compositeConstraint;
        }
        if (!constraint) {
            newConstraint.delegate = self;
            // constraints 存放的是单次执行的结果
           /*
            * make.left.top.equalTo(self.view).offset(60);
            * make.right.bottom.equalTo(self.view).offset(-60);
            * 就是执行个数为两个,每次结果都是 MASCompositeConstraint
            *
            */
            
            [self.constraints addObject:newConstraint];
        }
        return newConstraint;
    }
    

    里面的判断很有意思,会得到前面提到的,单个执行约束超过1个以上就会得到一个MASCompositeConstraint对象,只有一个约束就是MASViewConstraint对象。

    make.left,入参constraint = nil,返回值是MASViewConstraint

    make.left.top,入参constraint = make.left = MASViewConstraint,返回值就是MASCompositeConstraintMASCompositeConstraint.childConstraints = @[MASViewConstraint0 (left) ,MASViewConstraint1 (top)](每个约束对象都是MASViewConstraint),具体实现如下:

    // MASConstraint.h
    // 此处 self = make.left = MASViewConstraint
    - (MASConstraint *)top {
        return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop];
    }
    
    // MASViewConstraint.h
    // 工厂方法FUNC1判断里面,maker作为了MASViewConstraint的代理,也作为了MASCompositeConstraint的代理
    // 此处 self.delegate = MASConstraintMaker
    // 所以又回到工厂方法FUNC1了,这也是链式语法串起来的意义
    - (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
        NSAssert(!self.hasLayoutRelation, @"Attributes should be chained before defining the constraint relation");
    
        return [self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
    }
    
    

    大胆假设make.left.top.right ,入参constraint = make.left.top = MASCompositeConstraint, MASCompositeConstraint.childConstraints = @[MASViewConstraint0 (left) ,MASViewConstraint1 (top) ,MASViewConstraint2 (right)],返回值是MASViewConstraint具体实现如下:

    // MASConstraint.h
    // self = make.left.top = MASCompositeConstraint
    - (MASConstraint *)right {
        return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRight];
    }
    
    // MASCompositeConstraint.h
    - (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
        [self constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
      //返回自身
        return self;
    }
    - (MASConstraint *)constraint:(MASConstraint __unused *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
        // 还不明白为什么这样做,可能是怕释放掉
        id<MASConstraintDelegate> strongDelegate = self.delegate;
        // FUNC1 能找到maker作为了MASCompositeConstraint的代理   
        // self.delegate = MASConstraintMaker ,也回到了FUNC1
        MASConstraint *newConstraint = [strongDelegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
        // 此处代理是为后面的secondAttribute设置用的
        newConstraint.delegate = self;
        // 添加约束到数组,例子中是right的约束
        [self.childConstraints addObject:newConstraint];
        // 返回值是MASViewConstraint
        return newConstraint;
    }
    

    四个约束可以自己尝试下。

    3 、添加约束

    equalTo就比较简单,主要是添加secondViewAttribute、layoutRelation。
    offset也比较简单,是添加layoutConstant

    所有约束的属性值都有了,就需要添加到对应的firstViewAttribute.view上去了,主要是调用到install

    - (NSArray *)install {
        if (self.removeExisting) {
            NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view];
            for (MASConstraint *constraint in installedConstraints) {
                [constraint uninstall];
            }
        }
        NSArray *constraints = self.constraints.copy;
        for (MASConstraint *constraint in constraints) {
            constraint.updateExisting = self.updateExisting;
            [constraint install];
        }
        [self.constraints removeAllObjects];
        return constraints;
    }
    

    可以看到install的时候,会去遍历数组constraints,而constraints里面存放的是单次执行的MASConstraint对象,前面有说到,单次执行是1个约束的时候得到的是MASViewConstraint,单次执行1个约束以上的时候得到是MASCompositeConstraint

    其实最终都会走向MASViewConstraint的install方法。这个方法就是先拼装出一个NSLayoutConstraint对象,然后添加到对应的View

    自此,简单的Masnory使用就分析完了,后面还会补充其他部分。

    相关文章

      网友评论

          本文标题:理解Masnory—Part1

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