Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >iOS学习——iOS 宏(define)与常量(const)的正确使用

iOS学习——iOS 宏(define)与常量(const)的正确使用

作者头像
mukekeheart
发布于 2019-09-29 02:32:36
发布于 2019-09-29 02:32:36
1.9K00
代码可运行
举报
运行总次数:0
代码可运行

概述

  在iOS开发中,经常用到宏定义,或用const修饰一些数据类型,经常有开发者不知怎么正确使用,导致项目中乱用宏与const修饰。你能区分下面的吗?知道什么时候用吗?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define HSCoder @"汉斯哈哈哈"
NSString *HSCoder = @"汉斯哈哈哈";
extern NSString *HSCoder;
extern const NSString *HSCoder;

static const NSString *HSCoder = @"汉斯哈哈哈";

const NSString *HSCoder = @"汉斯哈哈哈";
NSString const *HSCoder = @"汉斯哈哈哈";
NSString * const HSCoder = @"汉斯哈哈哈";

当我们想全局共用一些数据时,可以用宏、变量、常量

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//宏
#define HSCoder @"汉斯哈哈哈"

//变量
NSString *HSCoder = @"汉斯哈哈哈";

//常量,四种写法
static const NSString *HSCoder = @"汉斯哈哈哈";
const NSString *HSCoder = @"汉斯哈哈哈";
NSString const *HSCoder = @"汉斯哈哈哈";
NSString * const HSCoder = @"汉斯哈哈哈";

宏、变量、常量之间的区别

  • 宏:只是在预处理器里进行文本替换,没有类型,不做任何类型检查,编译器可以对相同的字符串进行优化。只保存一份到 .rodata 段。甚至有相同后缀的字符串也可以优化,你可以用GCC 编译测试,"Hello world" 与 "world" 两个字符串,只存储前面一个。取的时候只需要给前面和中间的地址,如果是整形、浮点型会有多份拷贝,但这些数写在指令中。占的只是代码段而已,大量用宏会导致二进制文件变大
  • 变量:共享一块内存空间,就算项目中N处用到,也不会分配N块内存空间,可以被修改,在编译阶段会执行类型检查
  • 常量:共享一块内存空间,就算项目中N处用到,也不会分配N块内存空间,可以根据const修饰的位置设定能否修改,在编译阶段会执行类型检查

  我们来看一段代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define avatar @"60"
    if (false) {
        #define avatar @"80"
    }
    NSLog(avatar);

  这段代码会输出多少,我们将“avatar”定义为了60,然后在一个永远不会执行的代码里面重新定义了“avatar”为80,if语句中的代码永远不会执行,但是在编译时期,编译器会编译这段代码,而这个时候编译器就会将avatar这个名字替换为@“80”,所以这段代码最后的输出结果就是80。   当然这个时候编译器是会有一个警告的,但是不知道有多少同学会忽略这个警告。或者你会告诉我你对警告十分敏感,不会放过他的,但是记住你不是一个人在写代码,可能在别人的页面他给你重新定义了你的define,给你挖了一个大坑,还找不着.........

  所以还是尽量使用const,看苹果api也是使用常量多点,如下图:

const的用法

  const修饰符定义的变量是不可变的,比如说你需要定义一个动画时间的常量,你可以这么做:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static const NSTimeInterval kAnimateDuration = 0.3;

  当你试图去修改“ kAnimateDuration”的值的时候,编译器会报错。更加重要的是用这种方法定义的常量是带有类型信息的,而这点则是define不具备的。也许你已经发现了,如果你像如下这样定义,你是可以修改userName的值的,(说好的常量呢~~~)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static const NSString * kUserName = @"StrongX";

  首先我们需要确定的是以下的三种写法中前两种是一样的(可以修改kUserName的内容,也就是说const放在类型前还是类型后是一样的效果),第三种的效果不一样(无法修改kUserName的内容)。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static NSString const * kUserName = @"StrongX";
static const NSString * kUserName = @"StrongX";

static NSString  * const kUserName = @"StrongX";

  需要注意的是const 修饰的是他右边的部分,也就是说:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static NSString const * kUserName = static NSString const (* kUserName )

static NSString  * const kUserName = static NSString  * const (kUserName)

  当const修饰的是(userName)的时候,不可变的是userName。当const修饰的是( * )的时候,“*”在C语言中表示指针指向符,也就是说这个时候userName指向的内存块地址不可变,而内存保存的内容是可变的,我们来做个尝试:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
NSLog(@"内存地址: %x",& kUserName);
kUserName = @"superXLX";
NSLog(@"内存地址: %x",& kUserName);

以上NSLog会打印*userName指向的内存块地址,而他的输出如下图,我们已经发现当我们改变内存的内容的时候他的地址并没有发生改变,也就是说这是符合“const”修饰符的规定的。

所以当我们需要定义一个不可变的常量的时候 ,我们还是需要将“const”修饰符放到“*”指针指向符后边才对。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static NSString  * const kUserName = @"StrongX";

extern和static的用法

  在常量定义时我们经常会用到两个关键字,extern和static。那么这两个关键字的具体用法和作用是什么呢?下面我们就一起探究一下。

关键字extern

关键字extern主要是用来引用全局变量,它的原理是先在本文件中查找,查找不到再到其他文件中查找。用“extern”定义的常量必须也只能初始化一次,不满足必须以及只能一次的条件那么编译器就会提醒你。在定义全局变量的时候需要要注意你的命名,你可以使用规定好的前缀来命名。我们一般的用法是在.h文件中用extern申明一个常量名称,表示该常量可以让外部引用,然后在.m文件中对该常量进行初始化。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//在"constants.h"文件中,声明常量:
extern NSString *const XUserName;
代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//然后在“constants.m”中定义他:
NSString *const XUserName = @"StrongX";

关键字static

在探讨static的用法之前,我们首先需要了解两个概念:生命周期、作用域。

  • 生命周期:这个变量能存活多久,它所占用的内存什么时候分配,什么时候收回。
  • 作用域:这个变量在什么区域是可见的,可以拿来用的。

static分两种情况:修饰局部变量、修饰全局变量。

1、static修饰局部变量

  • 局部变量:在函数/方法/代码块内声明的变量。它的生命周期、作用域都是在这个代码块内。局部变量 存储在栈区(stack)一旦出了这个代码块,存储局部变量的这个栈内存就会被回收,局部变量也就被销毁。
  • 静态局部变量当用static修饰局部变量时,变量被称为静态局部变量,和全局变量,静态全局变量一样,是存储在‘静态存储区’。存储在 静态存储区 的变量,其内存直到 程序结束 才会被销毁。即,生命周期是整个源程序。

所以,静态局部变量的生命周期是整个源程序,但,作用域是声明它的代码块内

2、static修饰全局变量

  • 当全局变量没有使用static修饰时其存储在静态存储区,直到程序结束才销毁。也就是其作用域是整个源程序。我们可以使用extern关键字来引用这个全局变量。
  • 当全局变量使用static修饰时,其生命周期没有变,依旧是在程序结束时才销毁。但是其作用域变了。现在只限于申明它的这个文件才可见。使用extern关键字无法引用这个全局变量。
  • 全局变量本来是在整个源程序的所有文件都可见,static修饰后,改为只在申明自己的文件可见,即修改了作用域。即如果在.m文件中用static定义了常量,那么就不能在.h文件中使用extern进行外部申明。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//在.m文件中这样定义,则该常量只能在当前.m文件中使用,并且不能再.h文件中使用extern进行外部申明使用
static NSString * const kUserName = @"userName";

此外,常量的定义默认是extern类型的,上面已经说了const是用来定义一个常量。而static在C语言中(OC中延用)则表明此变量只在改变量的输出文件中可用(.m文件),如果你不加“static”符号,那么编译器就会对该变量创建一个“外部符号”所以如果你在两个互不相关的.m文件中定义了同名的常量,即使这两个文件之间没有任何关联,但是编译的时候会报错。报错类型如下

他会告诉你在两个目标文件(.0文件是.m文件编译后的输出文件)有一个重复的符号。(OC中没有类似C++中的名字空间的概念) 所以当你在你自己的.m文件中需要声明一个只有你自己可见的局部变量(k开头)的变量的时候一定要同时使用“static”和“const”两个符号。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-08-22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
iOS小技能: static、extern 存储类的应用(创建共享实例、申明公共方法、全局字符串常量)
在 C 语言中,程序内变量或函数的作用域和寿命是由其存储类确定的,比如static、extern。当 static 使得一个特定的文件中的函数和变量全局可见,extern 则使它们对所有文件可见。
公众号iOS逆向
2022/08/22
9660
iOS小技能: static、extern 存储类的应用(创建共享实例、申明公共方法、全局字符串常量)
iOS不可错过的关键字
建议查看原文:https://www.jianshu.com/p/dce05b24d288(不定时更新)
Dwyane
2018/09/30
4070
iOS不可错过的关键字
如何正确使用const(常量),define(宏)
前言 在开发中,也许我们会经常使用到宏定义,或者用const修饰一些数据类型,经常有开发者不知道怎么正确使用,导致项目中乱用宏定义与const修饰符。本篇主要介绍在开发中怎么正确使用const与define(宏定义) 当我们想定义全局共用的一些数据时,比如通知名字,动画时长等等,我们可以用宏、常量、变量: 宏: // 注意后面不需要带符号 #define ScottDidLoginSuccess @"登陆成功" 变量: // 注意后面一定要带符号 NSString *sc
Scott_Mr
2018/05/16
2.4K0
江哥带你玩转C语言 | 15- 修饰符和预处理指令
#include <stdio.h> int main(){ printf("hello lnj\n"); return 0; }
极客江南
2021/07/11
1.2K0
iOS学习——#define、const、typedef的区别
在iOS开发中经常遇到一些字段和类型的定义,例如配置生产和测试不同环境的参数等,这时候经常用到#define、const以及typedef。那么它们之间有什么区别呢?我们接下来一个一个具体了解下。
mukekeheart
2019/09/29
8680
深刻理解----修饰变量----关键字
1.extern关键字 基本解释:引用一个全局的量, extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。 使用介绍 ①全局的变量 在一个源文件里定义了一个变量,int i = 0; 在另一个文件里用 extern int i;就可以访问同一个变量i; ②全局的常量 在一个源文件里定义了一个常量,int const i = 0; 在另一个文件里用 extern const int i;就可以访问同一个常量i; ③坑 不能有重复定义的
用户1941540
2018/05/11
7250
[C语言] 常量与变量
说明:系统会在字符串尾部添加一个字符串作为结束字符"\0",那么如图所示的长度实际上是6的原因! 
謓泽
2022/12/12
7910
[C语言] 常量与变量
OC代码规范1——多用类型常量,少用#define预处理指令
两年前针对这一点写过一篇文章Effective Objective-C 2.0——多用类型常量,少用#define预处理指令,本文是在这篇文章的基础上进行扩展的。
拉维
2019/08/12
1.6K0
OC代码规范1——多用类型常量,少用#define预处理指令
【C语言】 C 语言 关键字分析 ( 属性关键字 | 常量关键字 | 结构体关键字 | 联合体关键字 | 枚举关键字 | 命名关键字 | 杂项关键字)
>**auto不能修饰全局变量** : auto 关键字不能修饰全局变量, 因为 ***auto 修饰的变量存储在栈内存中, 全局变量存储在全局区, 此时出现了冲突***. 如果使用auto修饰全局变量, 编译时会报错.
韩曙亮
2023/03/27
2.5K0
【C语言】 C 语言 关键字分析 ( 属性关键字 | 常量关键字 | 结构体关键字 | 联合体关键字 | 枚举关键字 | 命名关键字 | 杂项关键字)
完全入门C语言
C语言是一门通用的计算机编程语言,广泛应用于底层开发。其设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。有良好的跨平台的特性。 同时C语言还是面向过程的编程语言,这与JAVA C++等面向对象的编程语言有所不同。 目前主流的C语言编译器有:GCC Clang MVSVC等
Yui_
2024/10/15
2420
完全入门C语言
C++基础语法重点总结
函数重载指的是在同一个作用域中,声明了具有相同函数名的函数,它们的参数列表不同,也就是说参数类型不同,参数个数不同,参数顺序不同,返回值同不同都可以。
二肥是只大懒蓝猫
2023/10/13
2540
C语言基础知识:变量 (局部和全局)与常量(枚举常量.....)
1.变量的名称可以由字母,数字,下划线组成(不包括操作符);某些学校考试时可能会出现这样的代码:
aosei
2024/01/23
5950
C语言基础知识:变量 (局部和全局)与常量(枚举常量.....)
C++中变量声明与定义的规则
为了支持分离式编译,C++将定义和声明区分开。其中声明规定了变量的类型和名字,定义除此功能外还会申请存储空间并可能为变量赋一个初始值。
TOMOCAT
2021/04/20
2.7K0
嵌入式 C 语言(上)
嵌入式C语言和普通C语言在语法上几乎没有差别,其主要差别在于普通C语言的运行环境是OS之上,有很多的标准库函数支撑调用,分配的内存是电脑的内存,其处理器就是电脑的CPU;而在嵌入式环境中,会涉及到底层的硬件,而硬件本身是没有标准库可以调用的,因而就需要开发者使用C语言编程调试硬件,使其可以工作,对于开发某一款芯片,有针对的编译器(或者交叉编译环境),可以分配的内存则是芯片的RAM、Flash,处理器则是芯片自身带的MCU,例如ARM、DSP等。
韦东山
2022/05/05
1.5K0
嵌入式 C 语言(上)
1.C和C++区别,以及const分析(底层const/顶层const)
int f() int f(void)表示都一样,没有参数,返回值为int型的函数
诺谦
2019/05/24
9370
C语言学习
数据类型向内存申请空间图片图片#include <stdio.h>int main(){ char ch = 'A';//'ch'为变量 printf("%c",ch);//%c——打印字符格式的数据 return 0; }#include <stdio.h>int main(){ int age = 17;//'age'为变量 printf("%d",ch);//%d——打印整型十进制的数据 return 0; }变量局部变量全局变量定义:使用“=”为其赋值
暮云
2022/10/08
1.7K1
C语言学习
C语言里面那些你必须知道的常用关键字(详细讲解)
,在没有static修饰之前局部变量a是存放在栈区的。所以每次出局部范围就销毁(把空间还给操作系统)。然后,调用时重新创建初始化。
鸽芷咕
2023/12/25
3140
C语言里面那些你必须知道的常用关键字(详细讲解)
实践-小细节Ⅷ
const 这个单词翻译成中文是“常量”的意思。在程序中我们知道“常量”的值是不能变的,固定的。所以const关键字的作用就是:
進无尽
2018/09/12
4960
实践-小细节Ⅷ
程序员C语言快速上手——进阶篇(八)
过程式、模块化的C语言程序是由多个源文件(.c文件)构成的,在每一个源文件中,都形成一个文件作用域。所谓作用域,实际上就是指有效范围。一旦离开这个源文件的范围,就相当于离开了该源文件的文件作用域。在源文件中定义函数,那么在函数之外的地方,就属于全局作用域,即使是多个源文件,只要在函数之外,那它们就都属于全局作用域,全局作用域,全局都可访问。而在函数之内的空间声明变量,那它属于局部作用域。
arcticfox
2019/07/10
9860
初识C语言(3)
 C语言提供了丰富的关键字,这些关键字都是语言本身预先设定好的,用户自己是不能创造关键字的。
用户10923087
2024/01/23
1560
初识C语言(3)
推荐阅读
相关推荐
iOS小技能: static、extern 存储类的应用(创建共享实例、申明公共方法、全局字符串常量)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验