iOS进阶之masonry细致入微_MASUtilities.h

这个文件区别于Masonry.h文件。打个比方:Masonry主外,是个爷们。MASUtilities主内,是个姑娘。

接下来,让我们来了解一下这个浑身上下都是“干货”的姑娘。
#import <Foundation/Foundation.h>

#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 NSEdgeInsets

    typedef 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

/**
 *  Allows you to attach keys to objects matching the variable names passed.
 *
 *  view1.mas_key = @"view1", view2.mas_key = @"view2";
 *
 *  is equivalent to:
 *
 *  MASAttachKeys(view1, view2);
 */
#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];                                                 \
        }                                                                         \
    }

/**
 *  Used to create object hashes
 *  Based on http://www.mikeash.com/pyblog/friday-qa-2010-06-18-implementing-equality-and-    hashing.html
*/
#define MAS_NSUINT_BIT (CHAR_BIT * sizeof(NSUInteger))
#define MAS_NSUINTROTATE(val, howmuch) ((((NSUInteger)val) << howmuch) | (((NSUInteger)val) >> (MAS_NSUINT_BIT - howmuch)))

/**
 *  Given a scalar or struct value, wraps it in NSValue
 *  Based on EXPObjectify: https://github.com/specta/expecta
 */
/**
 *  inline关键字用来定义一个类的内联函数,引入它的主要原因是用它替代C中表达式形式的宏定义。
 */
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;
}

#define MASBoxValue(value) _MASBoxValue(@encode(__typeof__((value))), (value))
1, 先看脸。这位姑娘不仅有一张白皙的脸蛋,还情商极高。
 ///见人说人话,见鬼说鬼话
 ///这里你要了解苹果公司的生态,不仅仅有iphone还有TV和mac。这也表明了,这个库是可以伺候这三大平台的。
 #if TARGET_OS_IPHONE || TARGET_OS_TV
 #elif TARGET_OS_MAC
 #endif
///但愿你不是一个死读书的孩子。我上一篇文章列举了#define很多缺点。你不会看到这几行代码就吐槽我或者吐槽这个库的作者吧。你要理解,这两个地方是完全不同的两种考虑。
#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;
比较一下if里的代码和else if里面的代码。是不是很容易发现一个问题,if里面定义了MAS_VIEW_CONTROLLER而else if里面却没有。对,这个姑娘还很聪明。这里的用处,我留给聪明的你。你最好自己思考一下,我在后面的文章会回顾这里,到时候,看看我们是否心有灵犀。(郑重声明:我不是大神,也没有故意摆架子,更没有逗你玩。我是希望能够让你有提升,好对得起你与我的这份缘分)。
2, 再看胸。这里拒绝评论。
/**
 *  Allows you to attach keys to objects matching the variable names passed.
 *  view1.mas_key = @"view1", view2.mas_key = @"view2";
 *  is equivalent to:
 *  MASAttachKeys(view1, view2);
 */
#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];                                                 \
    }                                                                         \
}
这个宏简直就是一件作品。
先说作用:这个宏是为了给每一个view一个名字。干什么用?当你约束添加有误时,会有错误提示,但却无法告诉你哪一个view的约束出现了问题。然后你开始一个一个的排查。大部分情况下你的运气比较好,很快就找到问题。但每个月总有那么几天,你感觉浑身无力,头脑发热,怎么也找不到哪里约束出了问题。怎么办?如何找到这个有问题的约束,然后你应该看代码。对,源码面前了无秘密。然后你就发现了这个宏。然后你就知道了这个宏是干啥用的。那么,现在你是不是已经猜到了这个宏是干什么的了。这个宏会将当前view对象的名字转化为字符串,并赋值给view的一个属性。当这个view的约束报错时,会打印出这个字符串,让开发者快速定位到出问题的view。
这里定义的是一个方法,用宏的方式定义方法。
NSDictionaryOfVariableBindings看明白这个的作用了吗?
 NSDictionaryOfVariableBindings(v1,v2,v3)相当于[NSDictionary dictionaryWithObjectsAndKeys:v1,@“v1”,v2,@“v2”,v3,@“v3”,nil];
断言

NSAssert([obj respondsToSelector:@selector(setMas_key:)], @"Cannot attach mas_key to %@", obj);

如果发现obj这个对象没有实现对应的setMas_key方法,这里会抛异常出来!看一眼异常信息,慢慢体会断言的作用。
LGMasonryDome[6607:1939185] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Cannot attach mas_key to <ViewController: 0x104e08770>'
重点了!!!!!!
obj是如何拥有mas_key这个属性的。是系统的吗?看一眼前缀mas,难道是巧合,别闹了,这个明显是这个库的前缀嘛。既然不是系统的,那作者是通过何种手段为其添加的mas_key属性呢?

答案是分类+runtime

分类请亲自行百度的,这是个比较重要的概念。分类的理解不难,但结合代码构建过程,慢慢的就会体会出其巨大的威力。我曾见过有人用这个特性做组建开发。
我们来看下作者的源码。源码面前了无秘密。
- (id)mas_key {
    return objc_getAssociatedObject(self, @selector(mas_key));
}
- (void)setMas_key:(id)key {
    objc_setAssociatedObject(self, @selector(mas_key), key, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
runtime也是一个很大的概念,关于它的争论也很多。总的来说,理解方面你应该比较清晰的知晓其内部构造。这个是你理解OC这门语言的基础和难点。但使用方面,我的观点是尽量克制。
如果要论证,mas_key作为成员变量被添加到obj,那这也是个比较大的概念。要清楚一个对象的runtime组成,了解runtime的API,理解obj和mas_key的内存分布关系,我尽量在后期写一下这方 面的自己的理解。如果在这里扯,就偏离的我们的主题了。
3,我们先看腿,屁股就留在最后。我自己都觉得这么写是不是不合适啊。
/**
 *  inline关键字用来定义一个类的内联函数,引入它的主要原因是用它替代C中表达式形式的宏定义。
 */
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;
}
#define MASBoxValue(value) _MASBoxValue(@encode(__typeof__((value))), (value))
C++的代码,大家有时间可以了解一下。
这里的内联函数,相当于上面对于MASAttachKeys方法宏定义一样。这里你可以理解为C++中对方法的宏定义使用inline。
static inline id _MASBoxValue(const char *type, ...) 最后这三个点,是怎么回事?在C中,当我们无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表。
函数参数是以数据结构:栈的形式存取,从右至左入栈。
首先是参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈。因此栈底高地址,栈顶低地址。
举个例子如下:void func(int x, float y, char z);   
那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,因此,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。
这里给了第一个参数type。我们可以顺藤摸瓜了。
看看作者怎么摸腿的。哦,不,摸瓜。
下面是 <stdarg.h> 里面重要的几个宏定义如下:
typedef char* va_list;
void va_start ( va_list ap, prev_param ); /* ANSI version */
type va_arg ( va_list ap, type );
void va_end ( va_list ap );
va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。
<Step 1> 在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);
<Step 2> 然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数;
<Step 3> 然后是获取参数,调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置;
 <Step 4> 获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。说白了,就是让我们的程序具有健壮性。通常va_start和va_end是成对出现。
回过头再看看作者代码,是不是“又细又长”。
4,最后一步了。各位看客,办理VIP才可以一探究竟哦。这个作者好污。读这种技术文章,很头痛的。所以作者也算是煞费苦心了。
看看哪里使用了?
- (NSUInteger)hash {
    return MAS_NSUINTROTATE([self.view hash], MAS_NSUINT_BIT / 2) ^ self.layoutAttribute;
}
这是我最头痛的地方了。作者已经哭晕在厕所。请大家自行百度吧。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,012评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,628评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,653评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,485评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,574评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,590评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,596评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,340评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,794评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,102评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,276评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,940评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,583评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,201评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,441评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,173评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,136评论 2 352

推荐阅读更多精彩内容

  • 这个文件是整个库的头文件。顾名思义,使用时引入这个头文件,就可以使用这个文件中引入的所有文件。 先看源码 这个文件...
    天蓬大元阅读 1,819评论 1 0
  • 一,理清思路 二,深入实践 1,使用masonry,最常用的三个API 2,了解mas_makeConstrain...
    天蓬大元阅读 1,026评论 0 0
  • 本来没打算写总结的,无意间在简书看到了一位同行的总结,挺有意思的,想想写写总结其实挺不错的,这样可以理清楚一周都干...
    Timefiles阅读 256评论 0 0
  • 在路上 车轮飞旋 车轮上绑着希望 在路上 思绪飞扬 思绪里飘着梦想 车轮追赶着梦里的憧憬 岁月的辙印在身后拉长 在...
    忙里偷闲_9c1d阅读 209评论 0 1
  • 稽核不是管理人心,也不是改变人心,人心是虚幻的看不到的,所以只能感受不能改变。 当我们改变不了人心的...
    姜杨Ada阅读 90评论 0 0