![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=1739084455-LaLaIDYPZnt5JCwNNSVu6xmw06kuqflf-0-74b7ef50ff3abd3bc79c15761b784c60)
- 在MYClass.m文件中,实现printlocalVariable方法。在printlocalVariable方法内部,定义一个局部变量localVar,并赋初始值0。当方法被调用时,打印当前localVar的值,之后localVar值执行加1操作。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T81_9773.jpg?sign=1739084455-8j8E1dWOjE5bEip2uT1Lac5P9SwxUTOd-0-f4c9050f3c683fc7744dc73a1e12ecb7)
- main()函数中反复调用printlocalVariable方法。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T81_9775.jpg?sign=1739084455-XMfcmEaaAQxkY1mcQMPz5S5Ey0sSDkHZ-0-d238e96a6c9c702a2e873fcdbce6ab18)
打印结果如图3-18所示,当每次调用方法时,localVar都会被重新初始化赋值,因此每次打印值都为0。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P81_9777.jpg?sign=1739084455-R7WdXfpUWXmylFM0Ymwb0NBYW7snX8ZH-0-e87646694d3b9066063932c097423817)
图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=1739084455-pSIIc7sH5LyN3yhmmkW5lllp6JrrKnpa-0-34b4eca8c36e9d3839d08800fbfd4c11)
在main()函数中,调用printGlobalVariable方法,来检验内部全局变量gNum的值是累加的。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T82_9895.jpg?sign=1739084455-TPh0lU9IRJPgmuxXzNgLtQtuxd08y6pO-0-895fe8e865ac08581deb377bd8c20acd)
运行结果如图3-19所示。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P82_9897.jpg?sign=1739084455-Wd9CtEuNR6KY9RRQLtVBjR3TNUePSB9G-0-f2ee1a8d946723f985aac5fc96c597ad)
图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=1739084455-KdZQjUhJRwSfCVPArUu6cdJ7zCsJfJYN-0-7bf40bd9b51e09275a35133cb351943f)
- ClassB.m文件中,对全局变量gNum进行同样的声明。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T83_10045.jpg?sign=1739084455-mKu4E1FIOERcdkHoscgk36ryry7y2MBp-0-9500fdb8874c46425e2506fc614502a5)
- main.m文件中,调用MYClass类中定义的printGlobalVariable以及ClassA/ClassB中定义的printExternVar方法。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T83_82827.jpg?sign=1739084455-SmSc0L6URqJdR4lbG4lU8O7D9K2tFxWv-0-1fc5dfa04d3f2b3d0d8e0c87ad5fd3dd)
运行结果如图3-20所示。可以看到,声明+定义在MYClass类中的全局变量gNum,在ClassA和ClassB中的值是累加的。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P83_10050.jpg?sign=1739084455-W1T2ICT0e8maHpVjOzB8XMKsqb2ek8Z5-0-8ae98e50e23a8582fb343550fea13ef6)
图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=1739084455-4deQiimiVSXNqd4DRAaG3AMZlbAgTCaT-0-1cc08f3559d3641b54db55f70c2eade3)
当在main()中多次调用printStaticVariable方法时,staticValue的值会累加,如下所示。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T84_10159.jpg?sign=1739084455-aXjKmrzAMd3gxEggbfVBzFAhq6na5RbS-0-2d1f8ee98d5d4c09ea5c981473a3cb7c)
运行结果如图3-21所示。可以看到,在方法内部定义的静态变量值是累加的。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P84_10161.jpg?sign=1739084455-bnJTleP9hczU1XOsubhW3xfDJERL7ijG-0-d141d1840eef564816d483f2fa3c58cc)
图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=1739084455-FbAClWyMOc7L6SPhceOSpo2rNQrZRvvC-0-43d3bbe3920bb473ff9c2d82e45aba34)
在main()函数中,分别调用testStaticVarValue1方法和testStaticVarValue2方法。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T85_10307.jpg?sign=1739084455-tDkVCUcOWncrE1kPoWtmlLnYwLtdaJNx-0-8ac38e769328a93bb19108da999dcf27)
运行结果如图3-22所示。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P85_10309.jpg?sign=1739084455-d3a8x8EvRVEkD1grjrdM8eCKvHydL3E1-0-a557662ffcdb7927269d9a27cda50d5d)
图3-22 运行结果
3.静态变量的重要特性
静态变量在开发中有两个重要特性需要重点关注:
- 某个对象调用不同的方法,修改同一个静态变量时,则该静态变量的值是累加的。
- 当同一个类的不同对象,修改同一个静态变量时,则该静态变量的值也是累加的,见下面的示例代码。
在main()函数中,再创建一个MYClass对象,并调用printAndIncreaseStaticVarValue方法,可以验证,此时静态变量staticValue2的值也是叠加的。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T86_10419.jpg?sign=1739084455-aK61o92WMt22G5YhKfW4uwn9qNsYAQ2d-0-8d4dfdd5634899c114c77cb77d6e4c84)
运行结果如图3-23所示。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P86_10421.jpg?sign=1739084455-TinKTsQVsHJBsBNmbIXU6VqqzYOkd2kS-0-bc9b25e5dfd0df507532476416800c5d)
图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=1739084455-oTaAR4OohO4vKyOLaT3WHXKl3FekHKcA-0-2ae24528c9bf3adf2c20b6a13e1baf24)
对于const的使用,只要掌握一条原则即可,即:const用来修饰离其最近的变量。
如下面的代码所示。
- 当const修饰的是*x时,即指向字符串对象的指针指向是不能改变的,但是字符串的内容是可以改变的。
- 当const修饰的是y时,即指向的字符串内容@"九九学院"是不能改变的,但是指针指向的地址是可以改变的。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T87_10554.jpg?sign=1739084455-Vymc7dCztygW43iEx3BKwNtzphXIUOHt-0-0cb0fe90e85cb24e79d442c6dbc0724e)
运行结果如图3-24所示。被const修饰的指针变量*x不能改变,但该指针指向的存储内容可以被改变,同理,被const修饰的y,即存储了字符串的内存空间不能被修改,当修改时编译器会报错。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P87_10556.jpg?sign=1739084455-3epNDnIGpMj1lzFvupDb3UMuALJGgogX-0-401012fc7b16ed08a3463308f2cbdc4d)
图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=1739084455-nR4TGLiJbI6ii5qRQyBXJjdGl7qMw6Dg-0-df65575592e7ce1817635ce4adf262ef)
- 在MYConst.m文件中,为每个常量赋值。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T87_10562.jpg?sign=1739084455-DfneGwj8VqNRhe3FzuhHumqx62LpIQak-0-38ee3b89eb07d5d80d66f04ff0a2bcec)
- 当需要使用const修饰的常量时,引入MYConst类即可。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-T88_10646.jpg?sign=1739084455-GeeY5bngfoCierg2u7oE3IIqeF3firZE-0-1cf5babb1b44ec45d7a20d6d9e245368)
运行结果如图3-25所示。
![](https://epubservercos.yuewen.com/D4B438/15253388904120706/epubprivate/OEBPS/Images/Figure-P88_10648.jpg?sign=1739084455-SSpR6hXKmLUJCX0FpnLVUUx4i0eeETvs-0-e3bb93363939e90bba0cf7f05ae48b07)
图3-25 运行结果
3.const与宏#define的区别
在开发过程中,如果涉及字符串常量的定义,建议都用const,其处理性能比宏定义要高。当多次使用该常量时,只要在内存中创建一个对象即可。当使用宏#define来定义字符串常量时,在程序编译的过程中,所有使用到宏的地方都会使用设置的字符串来替换,因此当程序运行时,会创建多个对象,占用多个内存区域,执行效率低一些。
对于const和宏的使用,只要把握一个要点即可:凡是涉及常量的定义都建议用const,并且所有使用const修饰的常量都统一放在一个类中,其他的都可以使用宏来定义。