![iOS开发:从零基础到精通](https://wfqqreader-1252317822.image.myqcloud.com/cover/796/26793796/b_26793796.jpg)
3.5 变量
3.5.1 局部变量
1.局部变量简介
局部变量也称为内部变量,局部变量在方法内部声明,作用域仅仅限于方法内。有关局部变量在实际使用中,有以下几个常用的要点:
- 局部变量在方法内部定义,只有在方法运行时才存在。
- 局部变量没有默认的初始值,因此在使用前需要赋值。换句话说,当每次调用该方法时,局部变量都会被声明且初始化一次。
- 在一个方法中,方法中的输入参数也属于局部变量的范畴。
2.示例代码
在下面的示例代码中,在一个类的方法内部定义了一个局部变量,在方法内对该局部变量进行了修改,当每次调用该方法时,该局部变量的值都会被重新初始化。
- 定义一个MYClass类,在该类MYClass.h文件中添加一个printlocalVariable方法。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T81_9771.jpg?sign=1739544878-tSeTgaQt92uUoiHG2c5NPK9A0vS9fY5f-0-7ecc83203ce81e4af72ed87715b8f05f)
- 在MYClass.m文件中,实现printlocalVariable方法。在printlocalVariable方法内部,定义一个局部变量localVar,并赋初始值0。当方法被调用时,打印当前localVar的值,之后localVar值执行加1操作。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T81_9773.jpg?sign=1739544878-A5UB6pjtuAO2D6pomkTmNdV4pk1tQf4q-0-26d48e97cd7b00f5e4bf9ba259cc646a)
- main()函数中反复调用printlocalVariable方法。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T81_9775.jpg?sign=1739544878-o2dbkTYxoGELE0qQEDghUJd6In9zWSzc-0-5c89fc7fef584508126c296b40a2c68f)
打印结果如图3-18所示,当每次调用方法时,localVar都会被重新初始化赋值,因此每次打印值都为0。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P81_9777.jpg?sign=1739544878-omkFyBhtA3aJM5fc34ybPeDO6XzKhtml-0-29d6991d0fcacd7c12a8df05c7ffda90)
图3-18 打印结果
3.5.2 全局变量
全局变量也称为外部变量,它不属于任何一个方法,而是属于一个源程序文件或者特定的类。根据其作用域来区分,全局变量包括内部全局变量以及外部全局变量,其中,内部全局变量的作用域是整个类,而外部全局变量的作用域是整个程序。定义全局变量时,变量名建议以小写字母g开头。
1.内部全局变量
如果在程序开始处(如:类定义的头部)定义变量,那么就可以在类中任何位置都使用这个变量的值,且变量的值是累计变化的。这个时候,这个变量的作用域在于整个类的实现文件,称之为内部全局变量。
例如,在MYClass.m文件中定义一个内部全局变量gNum,并且赋初始值0,那么就可以在该类的所有方法中使用该变量,不需要重新声明,并且对于该变量值的修改是累计的。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T82_82825.jpg?sign=1739544878-Z3GoFWHmq1E93VZf0hGnQKfFLkPdycNV-0-f696b61e137e10d2afb8874fb0a982ac)
在main()函数中,调用printGlobalVariable方法,来检验内部全局变量gNum的值是累加的。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T82_9895.jpg?sign=1739544878-LYxlKBKKwy8RFoFCoIe0U7XRqKbzGtmU-0-bbe526e3d82c78ed365cf6036f2a7009)
运行结果如图3-19所示。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P82_9897.jpg?sign=1739544878-vnbA4j3AZb9kKWS9nUsGOuliEnezcY9z-0-c67adc6af0182f6e46105dedc139230c)
图3-19 运行结果
2.外部全局变量
外部全局变量,也是可以在程序的其他任何方法以及函数中访问的。这需要在访问外部全局变量的地方,声明变量类型以及名称(与定义时保持一致),并且添加extern关键字,即可访问该全局变量。
如下所示,可以定义一个新的类ClassA,在ClassA中的printExternVar方法中,首先声明全局变量,然后使用该全局变量。同时,可以再定义一个ClassB类,执行同样的操作。
- ClassA.m文件中,声明全局变量gNum,变量名称与MYClass中定义的全局变量保持一致,并且添加extern关键字。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T82_82826.jpg?sign=1739544878-DcB5tNTd9CvBypvwqMRp3ZHuUz9QGlwn-0-9f0446dc5f9b6468589e09690f1bbd06)
- ClassB.m文件中,对全局变量gNum进行同样的声明。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T83_10045.jpg?sign=1739544878-3WHNL1Htm9KOSq2JvFrJ223FiZkQCFa5-0-10b749da99e7df643f8e59a7441e166d)
- main.m文件中,调用MYClass类中定义的printGlobalVariable以及ClassA/ClassB中定义的printExternVar方法。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T83_82827.jpg?sign=1739544878-BnOkP60F2sCz5Ht6Kk8PkCoCnqHpVzrL-0-a4ae954ff55e2e1beee4155916291a55)
运行结果如图3-20所示。可以看到,声明+定义在MYClass类中的全局变量gNum,在ClassA和ClassB中的值是累加的。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P83_10050.jpg?sign=1739544878-zEcJhyZtRJTPWKU3xglhXoo9zOvotGYO-0-d644e4e092183fb170dfa0075f912b1f)
图3-20 运行结果
注意:需要区分变量的声明和定义,变量的声明不会引起内存空间的分配,而定义会分配内存空间。处理外部变量时,变量可以在很多地方声明为extern,但只能定义一次。如上例所示,gNum在ClassA和ClassB类中,分别进行了声明,但定义却是在MYClass类中完成的(gNum=0)。
3.5.3 静态变量
在Objective-C中,在变量声明前加上关键字static,该变量就成为静态变量。静态变量可以使局部变量保留多次调用同一个方法所取得的值。
1.在方法之内定义静态变量
静态变量只在程序开始执行时初始化一次。在不指定静态变量的值时,默认情况下,静态变量的初始值为0,并且多次调用方法时,保存这些数值。静态变量也可以在方法内部定义,此时,只能在该方法中使用定义的静态变量。
在下面的代码中,在MYClass类中添加printStaticVariable方法,并在方法内部定义静态变量staticValue,该静态变量只能在printStaticVariable方法中使用,并且staticValue的初始值为0。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T84_10157.jpg?sign=1739544878-Ig6c53Mp0FRzf5QJeQbcn1XhlYwAVllb-0-6f3002c0746f6d3148b60dd02500be51)
当在main()中多次调用printStaticVariable方法时,staticValue的值会累加,如下所示。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T84_10159.jpg?sign=1739544878-UjUiZn6QjSIBG3EkG7SwjWOgptog1Xe0-0-79d137021b516a498cfa32ead9b08eae)
运行结果如图3-21所示。可以看到,在方法内部定义的静态变量值是累加的。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P84_10161.jpg?sign=1739544878-Y3nIuSVC9lR7gwcvpTzu4sVB4EjmBpfa-0-8cdc3a5a05e5af092373a9290e858ea2)
图3-21 运行结果
2.在方法之外定义静态变量
静态变量除了可以在方法内部定义之外,还可以在方法之外定义,此时,该类的所有方法都可以访问该静态变量。
如下代码所示,在@implementation之外定义一个静态变量staticValue2,并赋初始值100。在该类中添加两个方法testStaticVarValue1和testStaticVarValue2,在这两个方法中都进行打印当前staticValue2的值,并且对staticValue2执行加1操作。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T85_82828.jpg?sign=1739544878-rmmXW3G6rjIxxBWwPtXnEAI63DOU9HQQ-0-9917a83fc268fd8f106b1baddfbdd4fd)
在main()函数中,分别调用testStaticVarValue1方法和testStaticVarValue2方法。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T85_10307.jpg?sign=1739544878-wYJsj16NKWsKrKZ2dwnn5poMR3Xm5V2j-0-fdfbc4e5c83fa9e5118ed534450619f0)
运行结果如图3-22所示。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P85_10309.jpg?sign=1739544878-FOee0J17EjR26g1S9VcfeWataDDKL3F5-0-a80dd25581377382611d82cfbfadaf69)
图3-22 运行结果
3.静态变量的重要特性
静态变量在开发中有两个重要特性需要重点关注:
- 某个对象调用不同的方法,修改同一个静态变量时,则该静态变量的值是累加的。
- 当同一个类的不同对象,修改同一个静态变量时,则该静态变量的值也是累加的,见下面的示例代码。
在main()函数中,再创建一个MYClass对象,并调用printAndIncreaseStaticVarValue方法,可以验证,此时静态变量staticValue2的值也是叠加的。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T86_10419.jpg?sign=1739544878-SjmajBV1m6tMmZ80ggbtl6AXn3su7PYi-0-baae67924047b040e2e15c9a75269f97)
运行结果如图3-23所示。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P86_10421.jpg?sign=1739544878-UTpPTmTo8RJIUU0nPrTEkov0DE4e9fLB-0-f531f22e297cd4657c10642255fc5bfd)
图3-23 运行结果
3.5.4 const关键字
1.const介绍
如果不想让某些变量的值改变,可以使用const关键字来修饰这些变量。如果添加const关键字,这些变量的值从头到尾都不会改变了。在iOS开发中,经常把字符串常量添加const关键字,从而替代宏(#define),因为const的执行性能比宏定义要高。给变量添加const关键字,主要目的是防止定义的对象被修改。在定义有const关键字的对象时,需要设置初始值。
在iOS开发中,有关const最常用的场景之一就是用来修饰NSString类型的字符串常量,这种使用方法在苹果提供的系统框架中随处可见,如下面的代码:
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T86_10426.jpg?sign=1739544878-cibZxWL28XBrcs0fl276zs4pYgZvZryn-0-a428ec379b01ef60a408b4808ceab6cc)
对于const的使用,只要掌握一条原则即可,即:const用来修饰离其最近的变量。
如下面的代码所示。
- 当const修饰的是*x时,即指向字符串对象的指针指向是不能改变的,但是字符串的内容是可以改变的。
- 当const修饰的是y时,即指向的字符串内容@"九九学院"是不能改变的,但是指针指向的地址是可以改变的。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T87_10554.jpg?sign=1739544878-dskk3SyNpUiEgnuT1E4svfhgttixqmHs-0-353126edbec7b0e06053f51e6f7e2ff3)
运行结果如图3-24所示。被const修饰的指针变量*x不能改变,但该指针指向的存储内容可以被改变,同理,被const修饰的y,即存储了字符串的内存空间不能被修改,当修改时编译器会报错。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P87_10556.jpg?sign=1739544878-meAmnfT3VYyG5TbOhB403daqpSoqpQs5-0-71fefc720eab9a02849530ffa5746dbf)
图3-24 运行结果
2.const使用方法
在实际的iOS开发中,const最常用于定义字符串常量,并且为了维护方便,会把工程中所有的字符串常量都统一放在一个const类中。具体的实现方法如下:
- 新建一个MYConst类,在MYConst.h文件中,声明所有的常量,需要注意一点:每个常量前面都加上extern关键字,否则在编译时会报错。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T87_10560.jpg?sign=1739544878-DRakSDL39AuWanbjt53RzwvvbAAMMbfq-0-19ca0330f821358008feeda4f64de22f)
- 在MYConst.m文件中,为每个常量赋值。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T87_10562.jpg?sign=1739544878-JmHmAU25BwIofxggBomkK9fHlzJ59eDZ-0-a2e5054499d79b452886a75b1f2fdba5)
- 当需要使用const修饰的常量时,引入MYConst类即可。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T88_10646.jpg?sign=1739544878-mbHgQJAT64TNHH1copgrm22jKEAtLXUI-0-37f365137e0425334b277444b80e9536)
运行结果如图3-25所示。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P88_10648.jpg?sign=1739544878-ScQCCeZFBoVYqAboqznmHt5UsthjHM30-0-0a11ebbbdef6290ca63c594a755ae7d4)
图3-25 运行结果
3.const与宏#define的区别
在开发过程中,如果涉及字符串常量的定义,建议都用const,其处理性能比宏定义要高。当多次使用该常量时,只要在内存中创建一个对象即可。当使用宏#define来定义字符串常量时,在程序编译的过程中,所有使用到宏的地方都会使用设置的字符串来替换,因此当程序运行时,会创建多个对象,占用多个内存区域,执行效率低一些。
对于const和宏的使用,只要把握一个要点即可:凡是涉及常量的定义都建议用const,并且所有使用const修饰的常量都统一放在一个类中,其他的都可以使用宏来定义。