一、在OC中的应用
static
关键字在 OC 中能够灵活地控制变量的生命周期和作用域,对于实现数据隐藏、状态管理等方面有着很重要的应用价值。
1.修饰局部变量
1.延长生命周期
当static
修饰局部变量时,该变量的生命周期会从原本所在代码块(比如函数内部)执行时存在,变为整个程序运行期间都存在。例如在一个函数中:
-(void)test{
static int count = 0;
count++;
NSLog(@"Count value: %d", count);
}
每次调用test
函数时,count
变量不会像普通局部变量那样重新初始化,而是会保留上次调用结束时的值,持续累加,这就是因为其生命周期被延长了,贯穿整个程序的运行阶段。
2.限制作用域
尽管生命周期变长了,但它的作用域依然局限在定义它的代码块(如函数内部等)内。在函数外部是无法直接访问这个被static
修饰的局部变量的,这保证了数据的局部可见性和一定程度的封装性,避免外部代码对其进行不恰当的修改。
2.修饰全局变量
使用 static
修饰的全局变量:限制作用域为当前文件:
对于全局变量来说,static
关键字可将其作用域限制在定义它的源文件(.m
文件或者.mm
文件等)内。正常情况下,全局变量在整个工程中只要包含了其定义所在的头文件(如果有对应的头文件声明),其他文件都能访问到它。但如果用static
修饰全局变量,比如:
// 在某个.m文件中定义
static NSString *privateString = @"This is private";
那么这个privateString
变量就只能在该.m
文件内部使用,其他源文件即使导入了相关的头文件(如果有的话)也无法访问该变量,起到了隐藏内部数据、增强模块独立性的作用。
全局变量使用 static
修饰与不使用 static
修饰存在以下区别:
未使用 static
修饰的全局变量:
其具有全局作用域,在整个程序的所有源文件中(只要经过正确的声明或外部引用等操作)都可以访问到该变量。例如,在一个 .m
文件中定义了一个普通的全局变量(未加 static
)
// 在文件A.m中定义全局变量
int globalVariable;
然后在另一个 .m
文件中,通过 extern
关键字进行声明后就可以使用这个变量:
// 在文件B.m中使用
extern int globalVariable;
- (void)someMethod {
globalVariable = 10;
}
这样的全局变量可以被多个不同的源文件共享和访问,便于在不同模块间传递数据,但也正因如此,如果使用不当可能导致命名冲突等问题,因为不同的源文件可能会定义同名的全局变量(尽管可以通过一些规范和命名策略尽量避免)。
3.应用于类方法(+ 方法)
在类的实现中,static
可以用来定义类方法中需要保持状态的变量,类方法是属于类而不是类的实例的方法。例如:
@implementation MyClass
+ (void)classMethod {
static int classLevelCount = 0;
classLevelCount++;
NSLog(@"Class level count: %d", classLevelCount);
}
@end
每次调用MyClass
类的classMethod
类方法时,classLevelCount
变量会维持其状态,实现类似计数等功能,并且其作用域局限在类方法这个范围内,外部不能随意访问,有助于管理类层面的一些私有数据或者状态信息。
总的来说,static
关键字在 OC 中能够灵活地控制变量的生命周期和作用域,对于实现数据隐藏、状态管理等方面有着很重要的应用价值。
二、在Swift中的应用
1.用于类型属性
1.存储型类型属性
使用 static
关键字可以定义属于类型本身(而非类型实例)的存储属性。例如,定义一个表示圆的类,其中 pi
这个常量作为所有圆实例都共用的属性,就可以用 static
来声明:
class Circle {
static let pi = 3.1415926
var radius: Double
init(radius: Double) {
self.radius = radius
}
func area() -> Double {
return Circle.pi * radius * radius
}
}
在这里,pi
是 Circle
类的一个存储型类型属性,通过 Circle.pi
就可以访问,它与具体某个 Circle
类的实例无关,所有 Circle
类的实例都共享这个 pi
的值,并且它在整个程序运行期间只会有一份存储,在内存中占据固定的空间。
2.计算型类型属性
同样可以定义计算型的类型属性,基于类型的其他属性或者外部条件等来计算出一个值。例如,对于一个表示矩形的结构体,定义一个计算型类型属性来获取其最大可能面积(假设边长有一定取值范围等条件):
struct Rectangle {
static var maxArea: Double {
// 这里假设一些计算逻辑,比如最大边长等情况
return 100.0
}
var width: Double
var height: Double
}
通过 Rectangle.maxArea
就能获取到这个计算出来的类型属性值,而且它也是属于整个 Rectangle
结构体类型层面的属性,不用创建 Rectangle
的实例就能访问。
2.用于类型方法
static
可以用来声明属于类型自身的方法,也就是可以直接通过类型来调用的方法,而不需要先创建该类型的实例。例如,对于一个工具类(通常用结构体来实现更合适),里面可以有一些静态方法来进行通用的操作:
struct MathUtils {
static func add(_ num1: Int, _ num2: Int) -> Int {
return num1 + num2
}
}
可以直接通过 MathUtils.add(5, 6)
这样的方式调用这个静态方法来执行加法运算,这种类型方法常用于提供一些与具体实例无关、通用的功能逻辑,比如数学运算、数据格式转换等操作。
3.在枚举(Enums)中的应用
在枚举中,static
可用于关联值(Associated Values)等相关操作,来让枚举具备更多灵活的特性。例如:
enum Result<T> {
case success(T)
case failure(Error)
static func handle(result: Result<Int>) {
switch result {
case.success(let value):
print("成功,值为: \(value)")
case.failure(let error):
print("失败,错误为: \(error)")
}
}
}
这里的 handle
静态方法可以方便地对 Result
枚举类型的不同情况进行统一处理,而不需要针对每个具体的枚举实例去编写重复的处理逻辑,增强了代码的复用性和逻辑性。
4.与类继承相关的特性(和 class
关键字对比)
在类中,如果要定义类型属性或者类型方法,既可以用 static
也可以用 class
,但二者有细微区别:
static
:用static
定义的类型属性和类型方法,在子类中是不能被重写的。例如:
class Animal {
static func makeSound() {
print("动物发出声音")
}
}
class Dog: Animal {
// 下面这行代码会报错,因为不能重写用static定义的类型方法
override static func makeSound() {
print("汪汪汪")
}
}
class
:而使用class
关键字定义的类型属性和类型方法,在子类中是可以被重写的,更适合那种希望在继承体系中允许子类根据自身特点来定制化相关类型层面行为的场景。例如:
class Vehicle {
class var maxSpeed: Double {
return 100.0
}
}
class SportsCar: Vehicle {
override class var maxSpeed: Double {
return 200.0
}
}