OC中的静态(static)/ 内联(inline)函数

起因

项目中用到了YYKit中的一些组件,比如YYTextYYImage,于是抽了点时间阅读了一下当中的一些代码。

发现了在YYKit中用了很多的函数方法,比如:

static inline UIEdgeInsets UIEdgeInsetRotateVertical(UIEdgeInsets insets) {
     UIEdgeInsets one;
     one.top = insets.left;
     one.left = insets.bottom;
     one.bottom = insets.right;
     one.right = insets.top;
     return one;
}

这种:

static void YYTextGetRunsMaxMetric(CFArrayRef runs, CGFloat *xHeight, CGFloat *underlinePosition, CGFloat *lineThickness) {
    CGFloat maxXHeight = 0;
    CGFloat maxUnderlinePos = 0;
    CGFloat maxLineThickness = 0;
    for (NSUInteger i = 0, max = CFArrayGetCount(runs); i < max; i++) {
        CTRunRef run = CFArrayGetValueAtIndex(runs, i);
        CFDictionaryRef attrs = CTRunGetAttributes(run);
        if (attrs) {
            CTFontRef font = CFDictionaryGetValue(attrs, kCTFontAttributeName);
            if (font) {
                CGFloat xHeight = CTFontGetXHeight(font);
                if (xHeight > maxXHeight) maxXHeight = xHeight;
                CGFloat underlinePos = CTFontGetUnderlinePosition(font);
                if (underlinePos < maxUnderlinePos) maxUnderlinePos = underlinePos;
                CGFloat lineThickness = CTFontGetUnderlineThickness(font);
                if (lineThickness > maxLineThickness) maxLineThickness = lineThickness;
            }
        }
    }
    if (xHeight) *xHeight = maxXHeight;
    if (underlinePosition) *underlinePosition = maxUnderlinePos;
    if (lineThickness) *lineThickness = maxLineThickness;
}

还有这种:

CGFloat YYTextScreenScale() {
    static CGFloat scale;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        scale = [UIScreen mainScreen].scale;
    });
    return scale;
}

以上三种函数使用相似、长相类似,但是仍然有些许的不同。
我们观察一下,可知:

1、第一个方法,使用了static和inline标示符,返回值显示为UIEdgeInsets类型。
2、第二个方法,只是只用了static,返回值为void类型。
3、第三个方法,干脆连static也省掉了,返回值为CGFloat。

那么三者到底有什么区别呢?往下看


分析

上面观察得出,三者基本上的区别就是标示符使用上的区别,那么我们分析下,表示符不同使用情况下,会有什么优势和好处。

一、引用inline标示符

引用inline标示符,能够使函数一作为一个标准的内联函数,函数的代码被放入符号表中,在使用时直接进行替换,(像宏一样展开)。

一般情况下引入内联函数是为了解决函数调用效率的问题,函数之间调用,是内存地址之间的调用,当函数调用完毕之后还会返回原来函数执行的地址,会有一定的时间开销,内联函数就是为了解决这一问题。
不用inline修饰的函数,汇编时会调用 call 指令,调用call指令就是就需要:

1)将下一条指令的所在地址入栈
2)并将子程序的起始地址送入PC(于是CPU的下一条指令就会转去执行子程序).

GCC中的inline函数可以相当于在一个普通的全局函数加上inline属性。inline关键字仅仅是建议编译器在编译的时候做内联展开处理,而不是强制在gcc编译器中,编译器可以忽略这个建议的,某一些情况下编译器会自动忽略这个inline,将这个函数还原成普通函数。如果编译选项设置为负无穷,即使是inline函数也不会被内联展开,除非设置了强制内联展开的属性(attribute((always_inline))),即NS_INLINE这个宏定义。

二、引用static标示符

通常情况下使用是用作声明静态变量。

1)修饰局部变量的时候,让局部变量只初始化一次,局部变量在程序中只有一份内存,但是并不会改变局部变量的作用域,仅仅是改变了局部变量的生命周期(只到程序结束,这个局部变量才会销毁)。
2)修饰全局变量的时候,全局变量的作用域仅限于当前文件。

当修饰函数的时候,对函数的连接方式产生影响,使得函数只在本文件内部有效,对其他文件是不可见的。这样的函数又叫作静态函数。使用静态函数的好处是,不需要担心在其他文件存在同名的函数从而产生干扰。

如果想要其他文件可以引用本地函数,则要在函数定义时使用关键字extern,表示该函数是外部函数,可供其他文件调用。另外在要引用别的文件中定义的外部函数的文件中,使用extern声明要用的外部函数即可。

另外也因为使用了static修饰,从而保证了不会不断地调用copy,保证了函数地址的一致性,减小了内存压力。

三、没有使用以上两个标识符修饰

简单浏览一下这个函数,我们可以发现这个函数是一个单例方法的构造,相对比较少见的用法,相对简洁,本仍然是一个简单的函数方法,可以被外界用CG_EXTERN调用。

用static修饰的函数,本限定在本源码文件中,不能被本源码文件以外的代码文件调用。而普通的函数,默认是extern的,也就是说,可以被其它代码文件调用该函数。


总结

一、为什么inline函数能取代宏?

1)#define定义的函数要有特别的格式要求,并不是每个人都能熟练使用,而使用`inline`则就行平常写函数那样。
2)和其他的宏定义一样,使用define宏定义的代码,编译器不会对其进行参数有效性检查,很容易出现无法察觉的错误,调试过程中会出现很多麻烦。
3)不仅是输入类型,#define宏定义的代码,返回值不能被强制转换成可转换的适合的转换类 。
4)#define是文本替换,需要在预编译时展开,内联函数是编译时候展开。

二、inline函数优点相比于普通函数:

1)inline函数避免了普通函数的,在汇编时必须调用call的缺点:取消了函数的参数压栈,减少了调用的开销,提高效率.所以执行速度确比一般函数的执行速度要快.
2)集成了宏的优点,使用时直接用代码替换(像宏一样);

三、inline内联函数的说明

1)内联函数只是我们向编译器提供的申请,编译器不一定采取inline形式调用函数。
2)内联函数不能承载大量的代码,如果内联函数的函数体过大,编译器会自动放弃内联。
3)内联函数内不允许使用循环语句或开关语句。
4)内联函数的定义须在调用之前。
5)当使用内联函数时,如果在多处调用了此内联函数,则此函数就会有N次代码段的拷贝,所以多配合`static`标示符使用。

Refrence

另外

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容