- iOS开发:从零基础到精通
- 史昕
- 2628字
- 2025-02-19 14:13:14
3.5 变量
3.5.1 局部变量
1.局部变量简介
局部变量也称为内部变量,局部变量在方法内部声明,作用域仅仅限于方法内。有关局部变量在实际使用中,有以下几个常用的要点:
- 局部变量在方法内部定义,只有在方法运行时才存在。
- 局部变量没有默认的初始值,因此在使用前需要赋值。换句话说,当每次调用该方法时,局部变量都会被声明且初始化一次。
- 在一个方法中,方法中的输入参数也属于局部变量的范畴。
2.示例代码
在下面的示例代码中,在一个类的方法内部定义了一个局部变量,在方法内对该局部变量进行了修改,当每次调用该方法时,该局部变量的值都会被重新初始化。
- 定义一个MYClass类,在该类MYClass.h文件中添加一个printlocalVariable方法。

- 在MYClass.m文件中,实现printlocalVariable方法。在printlocalVariable方法内部,定义一个局部变量localVar,并赋初始值0。当方法被调用时,打印当前localVar的值,之后localVar值执行加1操作。

- main()函数中反复调用printlocalVariable方法。

打印结果如图3-18所示,当每次调用方法时,localVar都会被重新初始化赋值,因此每次打印值都为0。

图3-18 打印结果
3.5.2 全局变量
全局变量也称为外部变量,它不属于任何一个方法,而是属于一个源程序文件或者特定的类。根据其作用域来区分,全局变量包括内部全局变量以及外部全局变量,其中,内部全局变量的作用域是整个类,而外部全局变量的作用域是整个程序。定义全局变量时,变量名建议以小写字母g开头。
1.内部全局变量
如果在程序开始处(如:类定义的头部)定义变量,那么就可以在类中任何位置都使用这个变量的值,且变量的值是累计变化的。这个时候,这个变量的作用域在于整个类的实现文件,称之为内部全局变量。
例如,在MYClass.m文件中定义一个内部全局变量gNum,并且赋初始值0,那么就可以在该类的所有方法中使用该变量,不需要重新声明,并且对于该变量值的修改是累计的。

在main()函数中,调用printGlobalVariable方法,来检验内部全局变量gNum的值是累加的。

运行结果如图3-19所示。

图3-19 运行结果
2.外部全局变量
外部全局变量,也是可以在程序的其他任何方法以及函数中访问的。这需要在访问外部全局变量的地方,声明变量类型以及名称(与定义时保持一致),并且添加extern关键字,即可访问该全局变量。
如下所示,可以定义一个新的类ClassA,在ClassA中的printExternVar方法中,首先声明全局变量,然后使用该全局变量。同时,可以再定义一个ClassB类,执行同样的操作。
- ClassA.m文件中,声明全局变量gNum,变量名称与MYClass中定义的全局变量保持一致,并且添加extern关键字。

- ClassB.m文件中,对全局变量gNum进行同样的声明。

- main.m文件中,调用MYClass类中定义的printGlobalVariable以及ClassA/ClassB中定义的printExternVar方法。

运行结果如图3-20所示。可以看到,声明+定义在MYClass类中的全局变量gNum,在ClassA和ClassB中的值是累加的。

图3-20 运行结果
注意:需要区分变量的声明和定义,变量的声明不会引起内存空间的分配,而定义会分配内存空间。处理外部变量时,变量可以在很多地方声明为extern,但只能定义一次。如上例所示,gNum在ClassA和ClassB类中,分别进行了声明,但定义却是在MYClass类中完成的(gNum=0)。
3.5.3 静态变量
在Objective-C中,在变量声明前加上关键字static,该变量就成为静态变量。静态变量可以使局部变量保留多次调用同一个方法所取得的值。
1.在方法之内定义静态变量
静态变量只在程序开始执行时初始化一次。在不指定静态变量的值时,默认情况下,静态变量的初始值为0,并且多次调用方法时,保存这些数值。静态变量也可以在方法内部定义,此时,只能在该方法中使用定义的静态变量。
在下面的代码中,在MYClass类中添加printStaticVariable方法,并在方法内部定义静态变量staticValue,该静态变量只能在printStaticVariable方法中使用,并且staticValue的初始值为0。

当在main()中多次调用printStaticVariable方法时,staticValue的值会累加,如下所示。

运行结果如图3-21所示。可以看到,在方法内部定义的静态变量值是累加的。

图3-21 运行结果
2.在方法之外定义静态变量
静态变量除了可以在方法内部定义之外,还可以在方法之外定义,此时,该类的所有方法都可以访问该静态变量。
如下代码所示,在@implementation之外定义一个静态变量staticValue2,并赋初始值100。在该类中添加两个方法testStaticVarValue1和testStaticVarValue2,在这两个方法中都进行打印当前staticValue2的值,并且对staticValue2执行加1操作。

在main()函数中,分别调用testStaticVarValue1方法和testStaticVarValue2方法。

运行结果如图3-22所示。

图3-22 运行结果
3.静态变量的重要特性
静态变量在开发中有两个重要特性需要重点关注:
- 某个对象调用不同的方法,修改同一个静态变量时,则该静态变量的值是累加的。
- 当同一个类的不同对象,修改同一个静态变量时,则该静态变量的值也是累加的,见下面的示例代码。
在main()函数中,再创建一个MYClass对象,并调用printAndIncreaseStaticVarValue方法,可以验证,此时静态变量staticValue2的值也是叠加的。

运行结果如图3-23所示。

图3-23 运行结果
3.5.4 const关键字
1.const介绍
如果不想让某些变量的值改变,可以使用const关键字来修饰这些变量。如果添加const关键字,这些变量的值从头到尾都不会改变了。在iOS开发中,经常把字符串常量添加const关键字,从而替代宏(#define),因为const的执行性能比宏定义要高。给变量添加const关键字,主要目的是防止定义的对象被修改。在定义有const关键字的对象时,需要设置初始值。
在iOS开发中,有关const最常用的场景之一就是用来修饰NSString类型的字符串常量,这种使用方法在苹果提供的系统框架中随处可见,如下面的代码:

对于const的使用,只要掌握一条原则即可,即:const用来修饰离其最近的变量。
如下面的代码所示。
- 当const修饰的是*x时,即指向字符串对象的指针指向是不能改变的,但是字符串的内容是可以改变的。
- 当const修饰的是y时,即指向的字符串内容@"九九学院"是不能改变的,但是指针指向的地址是可以改变的。

运行结果如图3-24所示。被const修饰的指针变量*x不能改变,但该指针指向的存储内容可以被改变,同理,被const修饰的y,即存储了字符串的内存空间不能被修改,当修改时编译器会报错。

图3-24 运行结果
2.const使用方法
在实际的iOS开发中,const最常用于定义字符串常量,并且为了维护方便,会把工程中所有的字符串常量都统一放在一个const类中。具体的实现方法如下:
- 新建一个MYConst类,在MYConst.h文件中,声明所有的常量,需要注意一点:每个常量前面都加上extern关键字,否则在编译时会报错。

- 在MYConst.m文件中,为每个常量赋值。

- 当需要使用const修饰的常量时,引入MYConst类即可。

运行结果如图3-25所示。

图3-25 运行结果
3.const与宏#define的区别
在开发过程中,如果涉及字符串常量的定义,建议都用const,其处理性能比宏定义要高。当多次使用该常量时,只要在内存中创建一个对象即可。当使用宏#define来定义字符串常量时,在程序编译的过程中,所有使用到宏的地方都会使用设置的字符串来替换,因此当程序运行时,会创建多个对象,占用多个内存区域,执行效率低一些。
对于const和宏的使用,只要把握一个要点即可:凡是涉及常量的定义都建议用const,并且所有使用const修饰的常量都统一放在一个类中,其他的都可以使用宏来定义。