![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=1739270038-x1vmtSOAcnuEiMVPNuj2G4nozTwjYdiD-0-6dcd5e7536fd3e0f7e0c10b506a52e0b)
- 在MYClass.m文件中,实现printlocalVariable方法。在printlocalVariable方法内部,定义一个局部变量localVar,并赋初始值0。当方法被调用时,打印当前localVar的值,之后localVar值执行加1操作。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T81_9773.jpg?sign=1739270038-rPatOLbqfeqMsZDUNAVtEgxbQ1n2Mckx-0-e277b5c826cac1d6b1a73e1307d9b76f)
- main()函数中反复调用printlocalVariable方法。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T81_9775.jpg?sign=1739270038-vJHHALURhUG5fDQwSvccXfATn1C8tqJw-0-a964e76b27b1db87a199aa25538620ac)
打印结果如图3-18所示,当每次调用方法时,localVar都会被重新初始化赋值,因此每次打印值都为0。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P81_9777.jpg?sign=1739270038-rS6R8807pfkRqyuITu2r51dxFGlWtHWU-0-b608918d0637603635a4fa944a0f3923)
图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=1739270038-aZ5241pVrIsmFoQcPj5sMfMNLorhCSqL-0-b2468066824626fdfe6d02a7cf0f48fd)
在main()函数中,调用printGlobalVariable方法,来检验内部全局变量gNum的值是累加的。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T82_9895.jpg?sign=1739270038-QFMHSry7M49E52zxMoRTFEO4aff7zxHs-0-60078b0de10a40169eff202f921b0dc8)
运行结果如图3-19所示。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P82_9897.jpg?sign=1739270038-3mxnC4X8gUqhMJuPLG8LUJYfjgK8Rn37-0-2d49bc5c137c57a21c0e4483739f05db)
图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=1739270038-yJWJU1tsZSbF7I4fs36ubSbqu0BzpCDk-0-8bbf282eaa2bfbac44b68c2a348c7a17)
- ClassB.m文件中,对全局变量gNum进行同样的声明。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T83_10045.jpg?sign=1739270038-zYOYbK58ioGVoaOB7BDeKr8EI4LcJmAr-0-5bfbbec6fa148c8853d8a0e1945307ed)
- main.m文件中,调用MYClass类中定义的printGlobalVariable以及ClassA/ClassB中定义的printExternVar方法。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T83_82827.jpg?sign=1739270038-D7p4ROGp11QKXUV1sqKW03YDm8BRNo65-0-53ff1ebdef6d920841b74b9675e04fcf)
运行结果如图3-20所示。可以看到,声明+定义在MYClass类中的全局变量gNum,在ClassA和ClassB中的值是累加的。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P83_10050.jpg?sign=1739270038-Rnxwuk6pxWqN8D4qvjUBiY3O6EbqzUD2-0-7d6a92818bfa33f4496a9f26bacba572)
图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=1739270038-m4D1rDECt7P3aStbNFXa69IiElZbp1Kb-0-0fad102cabe74ade8323173882108fc1)
当在main()中多次调用printStaticVariable方法时,staticValue的值会累加,如下所示。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T84_10159.jpg?sign=1739270038-WD4vdYXbcip5irlkcJcKhaabBfCBEDRj-0-3305fbaf92022be62764f4ec1f40867f)
运行结果如图3-21所示。可以看到,在方法内部定义的静态变量值是累加的。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P84_10161.jpg?sign=1739270038-qv0GukR9isYzHFMTkn9KlNf7tvwScDZB-0-70e8d321fbea44f8febff5b430a2d679)
图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=1739270038-fSpk558W7GXfGPhCksq3OHaJj97SCmIv-0-9e16a4ae3e09b13bf6b9567d482eb512)
在main()函数中,分别调用testStaticVarValue1方法和testStaticVarValue2方法。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T85_10307.jpg?sign=1739270038-SVLBY5tdjuHXK8wnqrgFc8OwCuFe03pA-0-8f6c3720bcfc36be5617701034440d7d)
运行结果如图3-22所示。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P85_10309.jpg?sign=1739270038-7XrCqEBTe2vn59Fx4562U42eYvpeY4iR-0-c5275ff900fea88afdf60e5fe3c67488)
图3-22 运行结果
3.静态变量的重要特性
静态变量在开发中有两个重要特性需要重点关注:
- 某个对象调用不同的方法,修改同一个静态变量时,则该静态变量的值是累加的。
- 当同一个类的不同对象,修改同一个静态变量时,则该静态变量的值也是累加的,见下面的示例代码。
在main()函数中,再创建一个MYClass对象,并调用printAndIncreaseStaticVarValue方法,可以验证,此时静态变量staticValue2的值也是叠加的。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T86_10419.jpg?sign=1739270038-dEhZ6BBEsRc4KCk8W43SsDfy4IzUwgz6-0-858d9a1804fbbddf28e4e8f37fdec596)
运行结果如图3-23所示。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P86_10421.jpg?sign=1739270038-XR88QFyJgOcWT2nnRTMTn3Woyh6GpR8x-0-c5648a72044a6675ef8efe3a046d4fe2)
图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=1739270038-ljUdovFapedGTPKsaN0m9pxzDLKxdLsp-0-72094bcbc08e73becdaea4acdda8c05f)
对于const的使用,只要掌握一条原则即可,即:const用来修饰离其最近的变量。
如下面的代码所示。
- 当const修饰的是*x时,即指向字符串对象的指针指向是不能改变的,但是字符串的内容是可以改变的。
- 当const修饰的是y时,即指向的字符串内容@"九九学院"是不能改变的,但是指针指向的地址是可以改变的。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T87_10554.jpg?sign=1739270038-B5lXY77ZYulGg67bFlciGNQG3Eo2vHFJ-0-e6aa6298b4fbbd9d142927a1e77db60a)
运行结果如图3-24所示。被const修饰的指针变量*x不能改变,但该指针指向的存储内容可以被改变,同理,被const修饰的y,即存储了字符串的内存空间不能被修改,当修改时编译器会报错。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P87_10556.jpg?sign=1739270038-lgt7HYDdZR12k7O5sntwarpRdh7gOZu6-0-048846024fe7c1c35f8ce64ebfb049a4)
图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=1739270038-a62IcTjW8CORUPAySLdtegTbSRksXvFB-0-44a56fbe2a867a45916366ce2f7f8dea)
- 在MYConst.m文件中,为每个常量赋值。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T87_10562.jpg?sign=1739270038-il0Oq9THn36LdXQT1Rte9zJTz5fc90AL-0-cd93061aa31f96cbe83a649446b1712d)
- 当需要使用const修饰的常量时,引入MYConst类即可。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T88_10646.jpg?sign=1739270038-rz8aU5ur6k00RduVEj9eRjvggJRFFnf4-0-3655cfc2d240dd14ec3b5ee8f06b99f1)
运行结果如图3-25所示。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P88_10648.jpg?sign=1739270038-lhh0i23FsOyBgWvhUQZkdgMTqqNbAM1M-0-9f46dfe187e72423596653d8097b6b23)
图3-25 运行结果
3.const与宏#define的区别
在开发过程中,如果涉及字符串常量的定义,建议都用const,其处理性能比宏定义要高。当多次使用该常量时,只要在内存中创建一个对象即可。当使用宏#define来定义字符串常量时,在程序编译的过程中,所有使用到宏的地方都会使用设置的字符串来替换,因此当程序运行时,会创建多个对象,占用多个内存区域,执行效率低一些。
对于const和宏的使用,只要把握一个要点即可:凡是涉及常量的定义都建议用const,并且所有使用const修饰的常量都统一放在一个类中,其他的都可以使用宏来定义。