![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=1738891181-3op4cvG5duEJEkLNzZvfwf4AtjNG6Q2p-0-c90a612ffddcde0baa621d0243687566)
- 在MYClass.m文件中,实现printlocalVariable方法。在printlocalVariable方法内部,定义一个局部变量localVar,并赋初始值0。当方法被调用时,打印当前localVar的值,之后localVar值执行加1操作。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T81_9773.jpg?sign=1738891181-asUkepVjdu6FA6F33PELnEtJWF9CYsfJ-0-225ce12fec3289cf5c703a45e90b40e5)
- main()函数中反复调用printlocalVariable方法。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T81_9775.jpg?sign=1738891181-kDLrXTM5CXrNyFmNEm7NdhVIwmG2YGHF-0-ca61ed17475d48bd85ce0baca7a60467)
打印结果如图3-18所示,当每次调用方法时,localVar都会被重新初始化赋值,因此每次打印值都为0。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P81_9777.jpg?sign=1738891181-qHkc4HIcXpDiKVbzlbTTY8gPOpyKW2xy-0-4e4507ec48ef1ef7934dc7bd005b0fdc)
图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=1738891181-aFrnwmaBFzjsdSF70K4O6LDSF8gVsPPZ-0-438bb5ee1c67b9ef15738911cf872068)
在main()函数中,调用printGlobalVariable方法,来检验内部全局变量gNum的值是累加的。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T82_9895.jpg?sign=1738891181-I6SkM0XofJgRahjbGDMkOAXQZS12UTMO-0-82f198789fd0416071dd49f7b78bea6e)
运行结果如图3-19所示。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P82_9897.jpg?sign=1738891181-3lOqLhx2M5kO2NX8ErrYbpGsvBtZIb3t-0-5a0b05f5f089144b9261a94414dfadf7)
图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=1738891181-Hu7zn45PqMQIZ7Ai7BPCuPkzJbqbQMr3-0-4f55b2bf8507aaa9af11700d8e9c0e2b)
- ClassB.m文件中,对全局变量gNum进行同样的声明。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T83_10045.jpg?sign=1738891181-lAlM6jg5gJsJdedbQJHtsDSn4TL3ggph-0-e2bea18740cf7ddd57db6e6ddfbecd5a)
- main.m文件中,调用MYClass类中定义的printGlobalVariable以及ClassA/ClassB中定义的printExternVar方法。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T83_82827.jpg?sign=1738891181-RKOg2ybquRTxhzxr9vjs2BqDyt94qy9t-0-69564c9297b7cef139e1492b74d3f81b)
运行结果如图3-20所示。可以看到,声明+定义在MYClass类中的全局变量gNum,在ClassA和ClassB中的值是累加的。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P83_10050.jpg?sign=1738891181-A1XCCxYQcqChPZnfpabzUbzcYmHFyDj6-0-5b0271f08f1f86ca1f6301890693e2a4)
图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=1738891181-pVgMvw0kTFi32FSmcmmzf25E72eVbrgu-0-1a1d4725d427aca527cbce126f136c09)
当在main()中多次调用printStaticVariable方法时,staticValue的值会累加,如下所示。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T84_10159.jpg?sign=1738891181-sKyWryVkUVVo4F9BGvQiI6Ez5BgLjoCZ-0-b929c65f93eac47479d7b72170e5f2a0)
运行结果如图3-21所示。可以看到,在方法内部定义的静态变量值是累加的。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P84_10161.jpg?sign=1738891181-loI8kLaZsAEXby4wr6248S2woNGFpiD0-0-aeb13dffcf504f507e97ef570af767ab)
图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=1738891181-PbypSYcCfYhPok0kyywH86rawPO2HoIE-0-1ac9a18973e9ec12dfe0b2e71f74e2b1)
在main()函数中,分别调用testStaticVarValue1方法和testStaticVarValue2方法。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T85_10307.jpg?sign=1738891181-Kb4QgI802pKBsuUq060bBs9RImgbxpmm-0-912ce24b59288cd70af06f79b9ac4ea2)
运行结果如图3-22所示。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P85_10309.jpg?sign=1738891181-OLrkA8MSAEh8tf405wpPKa60Ny6nXUZ2-0-a74138229bb1c81a6d0764a940969810)
图3-22 运行结果
3.静态变量的重要特性
静态变量在开发中有两个重要特性需要重点关注:
- 某个对象调用不同的方法,修改同一个静态变量时,则该静态变量的值是累加的。
- 当同一个类的不同对象,修改同一个静态变量时,则该静态变量的值也是累加的,见下面的示例代码。
在main()函数中,再创建一个MYClass对象,并调用printAndIncreaseStaticVarValue方法,可以验证,此时静态变量staticValue2的值也是叠加的。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T86_10419.jpg?sign=1738891181-HjxZ5EAaJ4N2PzKvizt3dAUQrpu9mHOr-0-d7474b4428543c1e4856e392d7d5165d)
运行结果如图3-23所示。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P86_10421.jpg?sign=1738891181-gkbxjMPqxJE0iqirRYk8TX3837jrit0r-0-3235cc38ce933f3d64cb5603eeffd431)
图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=1738891181-CFpd993o9sm4n5U6vAuJHoYDLyjljQrq-0-a7bc896a0da6e156507ccc2ea47675ae)
对于const的使用,只要掌握一条原则即可,即:const用来修饰离其最近的变量。
如下面的代码所示。
- 当const修饰的是*x时,即指向字符串对象的指针指向是不能改变的,但是字符串的内容是可以改变的。
- 当const修饰的是y时,即指向的字符串内容@"九九学院"是不能改变的,但是指针指向的地址是可以改变的。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T87_10554.jpg?sign=1738891181-OxULsMIVmFuDh7kuo6Cy1Rpppm3haUj0-0-79ee7d490ca1af6dee6942967f0e65fc)
运行结果如图3-24所示。被const修饰的指针变量*x不能改变,但该指针指向的存储内容可以被改变,同理,被const修饰的y,即存储了字符串的内存空间不能被修改,当修改时编译器会报错。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P87_10556.jpg?sign=1738891181-8F5vsWbXPySgv1TbvvnSWTl0I7OBlJiK-0-76af5eaee4b0b5f20b0384dbd9599c78)
图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=1738891181-1sLroBP95AlbF6SGc7WUc55HTK9s9pbY-0-46e172ceaa4dbf694fd16d3f87d71b0f)
- 在MYConst.m文件中,为每个常量赋值。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T87_10562.jpg?sign=1738891181-nAaSD6fLsNyWiLPENmZsN4bNyW92JTlY-0-95a07dd0358b5f4fce8912d1ef3551dc)
- 当需要使用const修饰的常量时,引入MYConst类即可。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T88_10646.jpg?sign=1738891181-nXWiwrBD7tFAVheuM5TTmp4Qsog1JicP-0-cfacab34f1753194f1cd2457bbaba0cf)
运行结果如图3-25所示。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P88_10648.jpg?sign=1738891181-4kwO42rG0HjkmYTZ9YA0Oo5kVI7BeCoh-0-d796cd4771f536ced0b650d84f4e7063)
图3-25 运行结果
3.const与宏#define的区别
在开发过程中,如果涉及字符串常量的定义,建议都用const,其处理性能比宏定义要高。当多次使用该常量时,只要在内存中创建一个对象即可。当使用宏#define来定义字符串常量时,在程序编译的过程中,所有使用到宏的地方都会使用设置的字符串来替换,因此当程序运行时,会创建多个对象,占用多个内存区域,执行效率低一些。
对于const和宏的使用,只要把握一个要点即可:凡是涉及常量的定义都建议用const,并且所有使用const修饰的常量都统一放在一个类中,其他的都可以使用宏来定义。