The release for this article is 1.1.0.

This class does some system-wide compatibility and provides some common methods.

1. System compatibility

#if TARGET_OS_IPHONE || TARGET_OS_TV

    #import <UIKit/UIKit.h>
    #define MAS_VIEW UIView
    #define MAS_VIEW_CONTROLLER UIViewController
    #define MASEdgeInsets UIEdgeInsets

    typedef UILayoutPriority MASLayoutPriority;
    static const MASLayoutPriority MASLayoutPriorityRequired = UILayoutPriorityRequired;
    static const MASLayoutPriority MASLayoutPriorityDefaultHigh = UILayoutPriorityDefaultHigh;
    static const MASLayoutPriority MASLayoutPriorityDefaultMedium = 500;
    static const MASLayoutPriority MASLayoutPriorityDefaultLow = UILayoutPriorityDefaultLow;
    static const MASLayoutPriority MASLayoutPriorityFittingSizeLevel = UILayoutPriorityFittingSizeLevel;

#elif TARGET_OS_MAC

    #import <AppKit/AppKit.h>
    #define MAS_VIEW NSView
    #define MASEdgeInsets NSEdgeInsetstypedef NSLayoutPriority MASLayoutPriority; static const MASLayoutPriority MASLayoutPriorityRequired = NSLayoutPriorityRequired; static const MASLayoutPriority MASLayoutPriorityDefaultHigh = NSLayoutPriorityDefaultHigh; static const MASLayoutPriority MASLayoutPriorityDragThatCanResizeWindow = NSLayoutPriorityDragThatCanResizeWindow; static const MASLayoutPriority MASLayoutPriorityDefaultMedium = 501; static const MASLayoutPriority MASLayoutPriorityWindowSizeStayPut = NSLayoutPriorityWindowSizeStayPut; static const MASLayoutPriority MASLayoutPriorityDragThatCannotResizeWindow = NSLayoutPriorityDragThatCannotResizeWindow;  static const MASLayoutPriority MASLayoutPriorityDefaultLow = NSLayoutPriorityDefaultLow; static const MASLayoutPriority MASLayoutPriorityFittingSizeCompression = NSLayoutPriorityFittingSizeCompression;#endif
Copy the code

In this part, three main aspects are compatible:

  • References to different header files on different systems.
  • The unification of different class names on different systems.
  • Unification of different layout priorities on different systems.

2. Debugging constraints conflict

Constraints conflict when setting constraints. For example, in the following code, I set both the left and right margins of the greenView and its width:

UIView *redView = [UIView new]; redView.backgroundColor = [UIColor redColor]; [self.view addSubview:redView]; [redView mas_makeConstraints:^(MASConstraintMaker *make) {make.size.mas_equalto (CGSizeMake(200.0, 100.0)); Make. Top. EqualTo (self. The view). Offset (200.0), and make the centerX. EqualTo (self. View);}]; UIView *greenView = [UIView new]; greenView.backgroundColor = [UIColor greenColor]; [self.view addSubview:greenView]; [greenView mas_makeConstraints:^(MASConstraintMaker *make) {make.height.mas_equalto (200.0); Make. Top. EqualTo (redView. Mas_bottom). Offset (30.0); make. Left. EqualTo (self. The view). Offset (15.0); Make. Right. EqualTo (self. The view). Offset (15.0); make the width. The mas_equalTo (750.0);}];Copy the code

At this point, if run, the console will print a bunch of conflicting constraints:

(
    <MASLayoutConstraint:0x6000000b8ea0 UIView:0x7fb48ed33dc0.left == UIView:0x7fb48ec03fd0.left + 15>,
    <MASLayoutConstraint:0x6000000b8f60 UIView:0x7fb48ed33dc0.right == UIView:0x7fb48ec03fd0.right - 15>,
    <MASLayoutConstraint:0x6000000b9080 UIView:0x7fb48ed33dc0.width == 750>,
    <NSLayoutConstraint:0x6040002819f0 UIView:0x7fb48ec03fd0.width == 375>
)
Copy the code

Although the information clearly tells you that the constraints conflict, which view is UIView: 0x7FB48ed33dc0, and which view is UIView: 0x7FB48EC03fd0? Look confused, don’t you? It’s easier to find when you have fewer constraints, but it’s harder to find once the view gets complicated.

At this point, this macro from Navigation will come in handy:

#define MASAttachKeys(...) \
    {                                                                             \
        NSDictionary *keyPairs = NSDictionaryOfVariableBindings(__VA_ARGS__);     \
        for (id key in keyPairs.allKeys) {                                        \
            id obj = keyPairs[key];                                               \
            NSAssert([obj respondsToSelector:@selector(setMas_key:)],             \
                     @"Cannot attach mas_key to %@", obj);                        \
            [obj setMas_key:key]; \} \}Copy the code

To use it, you just need to add this code:

MASAttachKeys(redView, greenView, self.view);
Copy the code

Run it again and you’ll notice that the printed content of the control strip has changed:

(
    <MASLayoutConstraint:0x6000000b0380 UIView:greenView.left == UIView:self.view.left + 15>,
    <MASLayoutConstraint:0x6000000b0440 UIView:greenView.right == UIView:self.view.right - 15>,
    <MASLayoutConstraint:0x6000000b0560 UIView:greenView.width == 750>,
    <NSLayoutConstraint:0x60000028b1d0 UIView:self.view.width == 375>
)
Copy the code

UIView: 0x7FB48ed33dc0 becomes greenView, and UIView: 0x7FB48EC03fd0 becomes self.view, which makes it easy to debug.

Actually this macro only did two things: one is for the system to provide the macro NSDictionaryOfVariableBindings (…). Made the encapsulation; Another thing I would like to do is to save user keys (e.g. RedView) by using the sorting View+MASAdditions provided by navigation.

3. Hash value of the object

#define MAS_NSUINT_BIT (CHAR_BIT * sizeof(NSUInteger))
#define MAS_NSUINTROTATE(val, howmuch) ((((NSUInteger)val) << howmuch) | (((NSUInteger)val) >> (MAS_NSUINT_BIT - howmuch)))
Copy the code

At first glance, it’s full of unrecognized macros and functions, but let’s look at it bit by bit:

  • CHAR_BIT: This macro represents onecharThe number of bits that the object occupies because of acharA byte, “byte,” represents the number of bits in a byte, usually 8.
  • Sizeof () :sizeofC/C++Operator, which simply returns the number of bytes of memory used by an object or type.

At this point, the first macro is clear: it calculates the number of bits in memory for the NSUInteger type on the current device.

Now that you understand the first macro, you understand the second: reverse val to the left and right as howMUCH. For example: val if 0110 0010, howmuch is 4. Calculation is the results of 0000 | 0000, 0110, 0010, 0010, 0110.

These macros generate hash values in the custom MASViewAttribute class.

4. Pack data

When we use a happy layout for navigation, we would like to say the following:

Make. Height. Mas_equalTo (200.0);Copy the code
Make. Size. Mas_equalTo (CGSizeMake (160.0, 90.0));Copy the code
make.edges.mas_equalTo(UIEdgeInsetsMake(0, 10, 20, 30));
Copy the code

We found that the mas_equalTo() macro is not picky about arguments and can pass almost any data type, thanks to what we’ll see next.


#define MASBoxValue(value) _MASBoxValue(@encode(__typeof__((value))), (value))
Copy the code

We’ll start with the macro: MASBoxValue(value) only needs to be passed a single argument. Inside the macro, we get the OC internal type string of the passed value and pass it along with the value to the _MASBoxValue function.


static inline id _MASBoxValue(const char *type, ...) {
    va_list v;
    va_start(v, type);
    id obj = nil;
    if (strcmp(type, @encode(id)) == 0) {
        id actual = va_arg(v, id);
        obj = actual;
    } else if (strcmp(type, @encode(CGPoint)) == 0) {
        CGPoint actual = (CGPoint)va_arg(v, CGPoint);
        obj = [NSValue value:&actual withObjCType:type];
    } else if (strcmp(type, @encode(CGSize)) == 0) {
        CGSize actual = (CGSize)va_arg(v, CGSize);
        obj = [NSValue value:&actual withObjCType:type];
    } else if (strcmp(type, @encode(MASEdgeInsets)) == 0) {
        MASEdgeInsets actual = (MASEdgeInsets)va_arg(v, MASEdgeInsets);
        obj = [NSValue value:&actual withObjCType:type];
    } else if (strcmp(type, @encode(double)) == 0) {
        double actual = (double)va_arg(v, double);
        obj = [NSNumber numberWithDouble:actual];
    } else if (strcmp(type, @encode(float)) = = 0) {float actual = (float)va_arg(v, double);
        obj = [NSNumber numberWithFloat:actual];
    } else if (strcmp(type, @encode(int)) == 0) {
        int actual = (int)va_arg(v, int);
        obj = [NSNumber numberWithInt:actual];
    } else if (strcmp(type, @encode(long)) == 0) {
        long actual = (long)va_arg(v, long);
        obj = [NSNumber numberWithLong:actual];
    } else if (strcmp(type, @encode(long long)) == 0) {
        long long actual = (long long)va_arg(v, long long);
        obj = [NSNumber numberWithLongLong:actual];
    } else if (strcmp(type, @encode(short)) == 0) {
        short actual = (short)va_arg(v, int);
        obj = [NSNumber numberWithShort:actual];
    } else if (strcmp(type, @encode(char)) == 0) {
        char actual = (char)va_arg(v, int);
        obj = [NSNumber numberWithChar:actual];
    } else if (strcmp(type, @encode(bool)) == 0) {
        bool actual = (bool)va_arg(v, int);
        obj = [NSNumber numberWithBool:actual];
    } else if (strcmp(type, @encode(unsigned char)) == 0) {
        unsigned char actual = (unsigned char)va_arg(v, unsigned int);
        obj = [NSNumber numberWithUnsignedChar:actual];
    } else if (strcmp(type, @encode(unsigned int)) == 0) {
        unsigned int actual = (unsigned int)va_arg(v, unsigned int);
        obj = [NSNumber numberWithUnsignedInt:actual];
    } else if (strcmp(type, @encode(unsigned long)) == 0) {
        unsigned long actual = (unsigned long)va_arg(v, unsigned long);
        obj = [NSNumber numberWithUnsignedLong:actual];
    } else if (strcmp(type, @encode(unsigned long long)) == 0) {
        unsigned long long actual = (unsigned long long)va_arg(v, unsigned long long);
        obj = [NSNumber numberWithUnsignedLongLong:actual];
    } else if (strcmp(type, @encode(unsigned short)) == 0) {
        unsigned short actual = (unsigned short)va_arg(v, unsigned int);
        obj = [NSNumber numberWithUnsignedShort:actual];
    }
    va_end(v);
    return obj;
}
Copy the code

The implementation logic of this function is simple: it simply boxes objects of type NSValue or NSNumber, depending on the type of argument passed in.

One thing to note is the implementation of mutable parameters.

When we’re writing code, we’re often exposed to mutable arguments, and the most common is NSLog(), where we can pass a lot of arguments. For example, in the following code, we pass four arguments:

NSLog(@"% @ % @ % @"The @"1"The @"2"The @"3");
Copy the code

So what do we do if we want to implement our own variable parameter methods? This is done using the C API: va_list, va_start, va_arg, and va_end.

Let’s write two ways to play:

  1. Write a method that prints all the arguments passed in:
- (void)print:(NSString *)string, ... {
    
    if(! string) {return;
    }
    
    NSLog(@"% @", string);
    
    va_list args;
    va_start(args, string);
    
    NSString *printString;
    
    while ((printString = va_arg(args, NSString *))) {
        NSLog(@"% @".printString);
    }
    
    va_end(args);
}
Copy the code

Usage:

[self print: @"1"The @"2"The @"3"The @"4"The @"5"The @"6"The @"Seven", nil];
Copy the code

Print result:

2018-08-07 17:47:26.312814+0800 WTMasonryDemo[111001:994417] 1 2018-08-07 17:47:26.312972+0800 WTMasonryDemo[11100:994417] 2 2018-08-07 17:47:26.313080+0800 WTMasonryDemo[11100:994417] 3 2018-08-07 17:47:26.313170+0800 WTMasonryDemo[11100/994417] 4 2018-08-07 17:47:26.313255+0800 WTMasonryDemo[11100/994417] 5 2018-08-07 17:47:26.313354+0800 WTMasonryDemo[111001:994417] 6 2018-08-07 17:47:26.313460+0800 WTMasonryDemo[11100:994417] 7Copy the code
  1. Let me write another one that looks like thisNSLog()Function method:
- (void)log:(NSString *)string, ... {
    
    if(! string) {return;
    }
    
    va_list args;
    va_start(args, string);
    
    NSString *logString = [[NSString alloc] initWithFormat:string arguments:args];
    NSLog(@"% @".logString);
    
    va_end(args);
}
Copy the code

Usage:

[self log: @"123% @ % @ % @"The @"4"The @"5"The @"6"];
Copy the code

Print result:

The 2018-08-07 17:50:23. 797442 + 0800 WTMasonryDemo (11139-999893), 123456Copy the code

5.MASLayoutConstraint

This class is a subclass of NSLayoutConstraint. When debugging constraint conflicts using the aforementioned macro MASAttachKeys, it will be easier to tell which constraints are set via navigation by printing them out on the console.

This class adds only one attribute:

@property (nonatomic, strong) id mas_key;
Copy the code

This property is due to holding the key that identifies the constraint object.