概述

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

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

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

//宏
#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修饰的位置设定能否修改,在编译阶段会执行类型检查

  我们来看一段代码

#define avatar @"60"
if (false) {
#define avatar @"80"
}
NSLog(avatar);

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

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

const的用法

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

static const NSTimeInterval kAnimateDuration = 0.3;

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

static const NSString * kUserName = @"StrongX";

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

static NSString const * kUserName = @"StrongX";
static const NSString * kUserName = @"StrongX"; static NSString * const kUserName = @"StrongX";

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

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

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

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

NSLog(@"内存地址: %x",& kUserName);
kUserName = @"superXLX";
NSLog(@"内存地址: %x",& kUserName);

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

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

static NSString  * const kUserName = @"StrongX";

extern和static的用法

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

关键字extern

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

//在"constants.h"文件中,声明常量:
extern NSString *const XUserName;
//然后在“constants.m”中定义他:
NSString *const XUserName = @"StrongX";

关键字static

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

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

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

1、static修饰局部变量

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

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

2、static修饰全局变量

  • 当全局变量没有使用static修饰时其存储在静态存储区,直到程序结束才销毁。也就是其作用域是整个源程序。我们可以使用extern关键字来引用这个全局变量。
  • 当全局变量使用static修饰时,其生命周期没有变,依旧是在程序结束时才销毁。但是其作用域变了。现在只限于申明它的这个文件才可见。使用extern关键字无法引用这个全局变量。
  • 全局变量本来是在整个源程序的所有文件都可见,static修饰后,改为只在申明自己的文件可见,即修改了作用域。即如果在.m文件中用static定义了常量,那么就不能在.h文件中使用extern进行外部申明。
    //在.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”两个符号。

iOS学习——iOS 宏(define)与常量(const)的正确使用的更多相关文章

  1. iOS 宏(define)与常量(const)的正确使用

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

  2. 【转】iOS 宏(define)与常量(const)的正确使用-- 不错

    原文网址:http://www.jianshu.com/p/f83335e036b5 在iOS开发中,经常用到宏定义,或用const修饰一些数据类型,经常有开发者不知怎么正确使用,导致项目中乱用宏与c ...

  3. 宏(define)与常量(const)

    http://mp.weixin.qq.com/s?__biz=MzAxMzE2Mjc2Ng==&mid=402470669&idx=1&sn=e34db91190d8d46f ...

  4. #define 和常量 const 的区别

    const 后的常量,程序对其中只能读不能修改. #include <iostream> using namespace std; int main() { const double pi ...

  5. iOS学习——iOS视频和推荐网站

    最近有人问有没有iOS学习的相关资料,就简单的把自己的知道的和资源共享一下: 个人感觉iOS开发人才饱和,培训泛滥,个人推荐后台升职空间大和web前端竞争小. [链接][Ronda收集整理]2014年 ...

  6. iOS学习——iOS项目Project 和 Targets配置详解

    最近开始学习完整iOS项目的开发流程和思路,在实际的项目开发过程中,我们通常需要对项目代码和资料进行版本控制和管理,一般比较常用的SVN或者Github进行代码版本控制和项目管理.我们iOS项目的开发 ...

  7. iOS学习——iOS原生实现二维码扫描

    最近项目上需要开发扫描二维码进行签到的功能,主要用于开会签到的场景,所以为了避免作弊,我们再开发时只采用直接扫描的方式,并且要屏蔽从相册读取图片,此外还在二维码扫描成功签到时后台会自动上传用户的当前地 ...

  8. iOS学习——iOS开发小知识点集合

    在iOS学习和开发过程中,经常会遇到一些很小的知识点和问题,一两句话就可以解释清楚了,这样的知识点写一篇随笔又没有必要,但是又想mark一下,以备不时之需,所以就有了本文.后面遇到一些小的知识点会不断 ...

  9. _编程语言_C++_宏定义#define 和 常量const 的区别

    C++中有两种定义常量的方式:#define预处理和const关键字 #define 预处理指令 #include <iostream> using namespace std; #def ...

随机推荐

  1. bs4——BeautifulSoup模块:解析网页

    解析由requests模块请求到的网页 import requests from bs4 import BeautifulSoup headers = {'User-Agent': 'Mozilla/ ...

  2. Python 3.5学习笔记(第二章)

    本章内容 1.模块 2.数据类型与数据运算 3.进制 4.byte 与 string 的互相转换 5.列表 6.元组 7.字符串操作 8.字典 一.模块 Python 把某些常用的定义存放在文件中,为 ...

  3. JDBC连接-操作数据库

    JDBC连接数据库的操作步骤 *条件:先启动mysql,然后创建新连接.这里我用Navicat工具来操作数据库. 前面是创建数据库,以及授权的问题.然后打开eclipse 这里我整理一下 抛出的两个异 ...

  4. python 2.7 - 3.5 升级之路 (二) : 语法与类库升级

    背景 在上一篇博文中,我们为升级python 2 -> 3已经做了一些准备.在这篇中,我们将针对语法与类库这两个方面进行讨论. 关于语法 1. print 在python3中, print 已经 ...

  5. Hive之函数与自定义函数

    系统自带的函数 1)查看系统自带的函数 hive> show functions; 2)显示自带的函数的用法 hive> desc function upper; 3)详细显示自带的函数的 ...

  6. django第三次(转自刘江)

    所有的模型字段都可以接收一定数量的参数,比如CharField至少需要一个max_length参数.下面的这些参数是所有字段都可以使用的,并且是可选的. null 该值为True时,Django在数据 ...

  7. pheatmap绘制“热图”,你需要的都在这

    热图可以聚合大量的数据,并可以用一种渐进色来优雅地表现,可以很直观地展现数据的疏密程度或频率高低. 本文利用R语言 pheatmap 包从头开始绘制各种漂亮的热图.参数像积木,拼凑出你最喜欢的热图即可 ...

  8. Unity经典游戏教程之:是男人就下100层

    版权声明: 本文原创发布于博客园"优梦创客"的博客空间(网址:http://www.cnblogs.com/raymondking123/)以及微信公众号"优梦创客&qu ...

  9. python_0基础开始_day05

    第五节 一.字典 python的数据结构之一 字典 —— dict 定义:dic = {"key":"dajjlad"} 作用:存储数据,大量,将数据和数据起到 ...

  10. 万万没想到,JVM内存结构的面试题可以问的这么难?

    在我的博客中,之前有很多文章介绍过JVM内存结构,相信很多看多我文章的朋友对这部分知识都有一定的了解了. 那么,请大家尝试着回答一下以下问题: 1.JVM管理的内存结构是怎样的? 2.不同的虚拟机在实 ...