【iOS】static、extern、const、auto关键字以及联合使用

目录


先了解一下静态变量所在的全局/静态区的特点:【iOS】内存五大分区

前言

上面提到的全局/静态区中存放的是全局变量或静态变量:

全局变量 是指变量值可以在运行时被动态修改
静态变量static修饰的变量,包含静态局部变量和静态全局变量

extern关键字

extern只能用来修饰全局变量,不能修饰其他变量,一般定义的全局变量默认带有extern,只不过被省略了,多用于跨文件引用

1. 与.h文件的关系

.h文件时为了更好地方便其他文件去调用本文件中的变量、属性和方法,作用同extern

如果在.h文件中声明全局变量,在OC中是不合法的,主要在@interface ExternModel : NSObject@end之间,当然如果在他们两个之外也是可以声明的:

应显式声明extern表明其不是成员变量,而是全局变量:

objectivec 复制代码
@interface ExternModel : NSObject
extern NSString* xyString;
@end

常见用法:

.h结合extern使用 :如果在.h文件中声明了extern全局变量,那么在同一个类中的.m文件对全局变量的赋值必须时:数据类型+变量名(与声明时一致) = XXXXX,并且在调用时导入.h文件:

objectivec 复制代码
//  ExternModel.h
#import <Foundation/Foundation.h>
@interface ExternModel : NSObject
extern NSString* xyString;
@end

//  ExternModel.m
#import "ExternModel.h"
@implementation ExternModel
NSString* xyString = @"Hello!!";
@end

例如,在viewController.mmain.m中调用,则可以引入ExternModel.h,否则无法识别全局变量

当然也可以通过不导入头文件的方式进行调用,通过extern调用,来获取全局变量的值

2. extern引用变量

如果在其他文件中访问一个类的全局变量,可以不用导入.h文件,通过extern去直接访问:

objectivec 复制代码
#import <Foundation/Foundation.h>
//#import "ExternModel.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
    //  只是用来获取全局变量(包括全局静态变量)的值,不能用于定义变量
        extern NSString* xyString;
        NSLog(@"%@", xyString);
    }
    return 0;
}
//  Hello!!

static关键字

static修饰变量后该变量就被定义成为一个静态变量

静态全局变量

static修饰全局变量,存储区仍在全局/静态区,但限定了该全局变量只能在当前源文件访问(从定义之处开始到文件结尾),限制了其作用域,在外部文件中是不可见的

好处: 定义后只会指向固定的指针地址,供当前文件使用,同一源程序的其他文件中可以使用相同名字的变量,不会发生冲突
不足: 存在的生命周期长,从定义直到程序结束
建议: 从内存优化和程序编译的角度来说,尽量少用全局静态变量,因为存在的生命周期长,一直占用空间。程序运行时会单独加载一次全局静态变量,过多的全局静态变量会造成程序启动慢

静态局部变量

static修饰局部变量,存储区由栈区变为静态区(.rwdata),延长了该局部变量的生命周期,虽然直到源程序结束该变量才会被销毁,但其作用域仍为局部,即当定义该变量的函数或者语句块结束时,就不可使用,尽管该变量仍存在

好处: 定义后只会存在一份值,只会初始化一次,每次调用都是使用的同一个对象内存地址的值,并没有重新创建,节省空间
不足: 存在的生命周期长,从定义直到程序结束
建议: 静态全局和局部变量从根本上没有什么区别,只是作用域不同而已。如果仅一个类中的对象和类方法使用并且值可变,可以定义全局静态变量

静态函数

在函数的返回类型前加上关键字static,函数就被定义成为静态函数

函数的实现和声明默认情况下是extern的,当函数被static修饰成为静态函数后,该函数的作用域仅限于定义它的源文件,同一程序的其他文件无法直接调用该函数,也无法通过外部链接访问

好处: 同一源程序的其他文件中可以使用相同名字的变函数,不会发生冲突;编译器可对static静态函数进行更多的优化,因为已知该函数不会在其他源文件中使用

const关键字

const是用来修饰常量(只读值)的,注意与宏的区别(EOC 2.0里面有提到《Effective Objective-C 2.0》读书笔记------熟悉Objective-C,良好的编码习惯提倡我们多用类型常量,少用#define类型常量):

宏的好处:宏能定义一些函数、方法 ;const不能

宏的坏处:使用大量宏,容易造成编译时间久每次都需要重新替换;宏不做检查,不会报编译错误,只是替换,const会编译检查,会报编译错误

const仅仅用来修饰右边的变量:

objectivec 复制代码
// 定义变量
int a = 1;
// 允许修改值
a = 20;
// const两种用法
// 1.const:修饰基本变量p
// 这两种写法是一样的,const只修饰右边的基本变量b
const int b = 20; // b:只读变量
int const b = 20; // b:只读变量
// 不允许修改值
b = 1;
// 2. const:修饰指针变量*p,带*的变量,就是指针变量
// 定义一个指向int类型的指针变量,指向a的地址
int *p = &a;
int c = 10;
// 允许修改p指向的地址
p = &c;
// 允许修改p访问内存空间的值
*p = 20;
// const修饰指针变量访问的内存空间,修饰的是右边*p1,
// 两种方式一样
const int *p1; // *p1:常量 p1:变量
int const *p1; // *p1:常量 p1:变量
// 允许修改p指向的地址
p1 = &a;
// 不允许修改p访问内存空间的值
*p1 = 27;
// const修饰指针变量p1
int * const p2; // *p1:变量 p1:常量
// 不允许修改p指向的地址
p2 = &a;
// 允许修改p访问内存空间的值
*p2 = 27;
// 第一个const修饰*p1 第二个const修饰 p1
// 两种方式一样
const int * const p3; // *p1:常量 p1:常量
int const * const p3; // *p1:常量 p1:常量
// p的指向和指向内存空间的值都不能修改
p3 = &a;
*p3 = 27;

联合使用

static和extern

extern引用变量时会先在当前文件查找有没有全局变量,没有找到,才会去其他文件查找

objectivec 复制代码
static int age = 21;

void test(void) {
    static int age = 21;
    age++;
    NSLog(@"%d", age);
}

int main(void) {
    test();
    test();
    
    extern int age;
    NSLog(@"%d", age);
    return 0;
}
// 打印结果:
// 22
// 23
// 21

static和const

声明一个只读的静态变量,常用于声明上面《EOC 2.0》提到的类型常量

objectivec 复制代码
// 开发中经常将无需改变的key值作为类型(字符串)常量
static NSString* const key1 = @"keyValue1";
static NSString const * key2 = @"keyValue2";
static const NSString* key3 = @"keyValue3";

extern和const

声明一个全局的类型常量,供多个文件共享

全局常量正规写法:开发中便于管理所有的全局变量,通常搞一个GlobeConst文件,里面专门定义全局变量,统一管理,要不然项目文件多不好找

objectivec 复制代码
// GlobeConst.h
extern NSString * const kGlobalConstKey;

// GlobeConst.m
NSString * const kGlobalConst = @"globalConst";

auto关键字

认识了这么多修饰变量的关键字,最后再来简单认识一下之前未了解过的auto关键字

变量被auto修饰后,编译器会根据初始化表达式自动推断出变量的类型:

objectivec 复制代码
auto x = 3.14;      // x 是浮点型 double
auto y = 520;       // y 是整形 int
auto z = 'a';       // z 是字符型 char
auto nb;            // error,变量必须要初始化
auto double nbl;    // 语法错误, 不能修改数据类型   
auto pi = new auto(42); // pi 的类型被推导为 int*
const auto& y = x; // y 的类型被推导为 const int&

auto关键字更适用于类型冗长复杂、变量使用范围专一时,使程序更清晰易读。如:

objectivec 复制代码
 std::vector<int> vect; 
 for (auto it = vect.begin(); it != vect.end(); ++it)
 {  //it的类型是std::vector<int>::iterator
    std::cin >> *it;
  }

或者保存lambda表达式类型的变量声明:

objectivec 复制代码
  auto ptr = [](double x){return x*x;};//类型为std::function<double(double)>函数对象
相关推荐
用户0918 小时前
SwiftUI Charts 函数绘图完全指南
ios·swiftui·swift
YungFan18 小时前
iOS26适配指南之UIColor
ios·swift
权咚1 天前
阿权的开发经验小集
git·ios·xcode
用户091 天前
TipKit与CloudKit同步完全指南
ios·swift
小溪彼岸2 天前
macOS自带截图命令ScreenCapture
macos
法的空间2 天前
Flutter JsonToDart 支持 JsonSchema
android·flutter·ios
2501_915918412 天前
iOS 上架全流程指南 iOS 应用发布步骤、App Store 上架流程、uni-app 打包上传 ipa 与审核实战经验分享
android·ios·小程序·uni-app·cocoa·iphone·webview
TESmart碲视2 天前
Mac 真正多显示器支持:TESmart USB-C KVM(搭载 DisplayLink 技术)如何实现
macos·计算机外设·电脑
00后程序员张2 天前
iOS App 混淆与加固对比 源码混淆与ipa文件混淆的区别、iOS代码保护与应用安全场景最佳实践
android·安全·ios·小程序·uni-app·iphone·webview
Magnetic_h2 天前
【iOS】设计模式复习
笔记·学习·ios·设计模式·objective-c·cocoa