厚积薄发之从Masonry 源码看Objective-C [开篇
前言: 这个系列可能不会分析,
Masonry
如何如何好, 估计多在分析源码写法, 不清楚的语法, 用了什么知识点, 可能是这样, 写成什么样也不确定,Masonry
是一个关于iOS开发的布局框架,Masonry
是对NSLayoutConstraint
的封装, 你知道的NSLayoutConstraint
这个家伙使用起来很麻烦的, 语法相当的啰嗦. 所以Masonry
简化了这个家伙的使用方式, 同时Masonry
有一个小兄弟, 也在成长中, 成长的也是不错, 你可以在Masonry
的家里找到这个家伙(SnapKit), 不同的是SnapKit 是使用Siwft
写的, 如果你都知道, 就当是我在凑字数(来 qq群打我好啦 认真脸), 今天就主要介绍一些Masonry
相关的干货, 反正你知道我是学习记录, 谈不上分享的, 我一贯就是这个态度的[囧].
下面的这张图, 简单介绍了Masonry
的主要大部分类, 你说: 没写全? 对的. 我能说没有地方画让我删了吗? 认真.
可能会写这样几篇
厚积薄发之从Masonry 源码看Objective-C [开篇]
[厚积薄发之从Masonry 源码看Objective-C [开篇 续]]
[厚积薄发之从Masonry 源码看Objective-C [开篇 续续]
[厚积薄发之从Masonry 源码看Objective-C [开篇 续续续]
[厚积薄发之从Masonry 源码看Objective-C [终篇 - 有始有终 希望最后能够完美]
功力有限写什么样, 算什么样. 有点死猪不怕烫的意思. 逃跑~.
目录
- Objective-C可变参数
- 内联函数 inline
- mas_equalTo这个宏的实现
可变参数: 在开始之前, 我决定先看看这个小知识, 当然你了解C 语言对此并不陌生, 然而我不像你, 我已经把以前学的知识, 交还给我的teacher了. 你可能也忘记了来跟我一起复习一遍, 在C 语言中的解释大致是这个样子的, 可变参数的实现必然不能缺少VA_LIST
,
VA_LIST 是在C语言中是这样解释的, 用来解决变参问题的一组宏,所在头文件:#include <stdarg.h>,用于获取不确定个数的参数
而在Objective-C中 同样也有关于处理不确定参数个数的实现使用va_list
相关, 接下来通过一段简单的代码进行演示.
可变参数在OC中如何实现?
-
va_list
定义一个va_list
变量, 这个变量的是指向参数的指针 -
va_start
使用宏 定义va_list
变量 -
va_arg
va_arg返回可变的参数,va_arg
的第二个参数是你要返回的参数的类型,如果函数有多个可变参数的,依次调用va_arg
获取各个参数 -
va_end
用va_end宏结束可变参数的获取
- (NSMutableArray *)vaListUsing:(NSString *)p1,...NS_REQUIRES_NIL_TERMINATION{
NSMutableArray *array = [NSMutableArray array];
// 第一个参数进数组
[array addObject:p1];
// 定义一个va_list变量, 这个变量的是指向参数的指针
va_list v;
id vStr;
// 使用宏定义va_list变量
va_start(v, p1);
// va_arg返回可变的参数,va_arg的第二个参数是你要返回的参数的类型,如果函数有多个可变参数的,依次调用va_arg获取各个参数
while ((vStr = va_arg(v, id))) {
[array addObject:vStr];
}
// 用va_end宏结束可变参数的获取
va_end(v);
return array;
}
NSMutableArray *array = [self vaListUsing:@"Cancel", @"Other",@"OK", nil];
NSLog(@"%@", array);
这是获取的打印结果, 以上就是我通过代码的形式表现出来的, 你可以通过获取的参数名, 参数个数, 可进行适当的UI布局什么的.
2016-11-23 15:58:09.225 Masonry阅读理解****[5338:1112595] (*
** Cancel,**
** Other,**
** OK**
)
以上是不是跑题了, 我的回答是 并没有! 你也相信我不能在这瞎扯淡.
接下来看看Masonry中如何使用这个小知识的呢.
在MASUtilities中
先看下面这段源码, 通过上面的介绍, 我觉的你可能有点能明白了, 稍后解释代码, 首先看看inline
C和C++语言中inline用来声明内联函数的, 我还是有些印象的 作用是是 用来替代C中表达式形式的宏定义
的. 而在OC用也是有同样的作用.
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))
由源代码引申之内联函数
定义: 内联函数是指用inline关键字修饰的函数
作用: 去掉函数调用带来的开销
这个说的可以 可以去看看
代码示例
仿照Masonry的示例代码, 我简单测试了一下, 准确性有待考察, 不过粗略的看, 貌似内联函数效率高一点, 你怎么看? 欢迎评论拍砖. 教我做人.
static inline
static inline int xtAdd(int x, int y){
int res = x + y;
return res;
}
#define RESXyAdd(x, y) xtAdd(x, y)
NSDate *tmpStartData = [NSDate date];
int res = RESXyAdd(2, 3);
// 内联
// 2016-11-24 14:06:13.816 Masonry解析[7209:1504811] >>>>>>>>>>cost time = 0.608981 ms
// 2016-11-24 14:00:57.229 Masonry解析[6870:1496409] >>>>>>>>>>cost time = 0.648022 ms
// 2016-11-24 14:01:38.670 Masonry解析[6898:1497238] >>>>>>>>>>cost time = 0.645995 ms
// 非内联
// 2016-11-24 14:03:57.975 Masonry解析[7052:1500940] >>>>>>>>>>cost time = 0.657976 ms
// 2016-11-24 14:04:57.955 Masonry解析[7101:1502351] >>>>>>>>>>cost time = 0.651002 ms
NSLog(@"res === %d", res);
double deltaTime = [[NSDate date] timeIntervalSinceDate:tmpStartData];
NSLog(@">>>>>>>>>>cost time = %f ms", deltaTime * 1000);
接下来通过Masonry的使用来解释上面提到的代码
首先我定义了一个View 距上左88 宽高88 我分别可以使用make.width.height.mas_equalTo(88)
make.width.height.mas_equalTo(@88)
或者 make.size.mas_equalTo(CGSizeMake(88, 88))
通过这个宏我分别可以实现相应很多操作. 其实这些都是通过上面提到的内联函数实现的. 先通过strcmp
这个库函数比较传进来的类型跟什么类型匹配, 之后放回相应的对象. 完成对象的校验. 这样的效率很高, 同时使用方便.
UIView *view = [UIView new];
[self.view addSubview:view];
view.backgroundColor = [UIColor redColor];
[view mas_makeConstraints:^(MASConstraintMaker *make) {
//
make.top.equalTo(self.view.mas_top).with.offset(88);
make.left.equalTo(self.view.mas_left).with.offset(88);
// 我通过这个宏 传递88给width 和 height
make.width.height.mas_equalTo(88);
// 或者 @88
// make.width.height.mas_equalTo(@88);
// 最终都是通过上述的内联函数实现了相应的类型对应
// make.size.mas_equalTo(CGSizeMake(88, 88));
}];
#
define mas_equalTo(...) equalTo(MASBoxValue((VA_ARGS)))
mas_equalTo是个上面这样表示的宏定义
#
define MASBoxValue(value) _MASBoxValue(@encode(typeof((value))), (value))
而 MASBoxValue 最终是上面这个宏定义, 而最终 _MASBoxValue就是最上面提到的一大坨代码(那个内联函数). 而不确定参数, 我通过文章的开头, 也进行了交代.
总结: 这个MASUtilities 文件 , 剩下就是一些重命名, 和宏定义的一些东西了, 这篇就到这, 篇幅太长是没有耐心看下去的, 起个抛砖引玉的作用, 先这样, 下一篇 还不知道怎么写, 尽力写的全面一点, 让自己有所收获.
如果有一丝丝帮助 点个关注喜欢 又何妨??
文/ 夏天然后
本文已在版权印备案,如需转载请访问版权印。36586216
获得授权