理解Masnory—Part1

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 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)