相关链接:
https://juejin.cn/post/7030079083723620388
https://www.jianshu.com/p/fc1b075002c1
1.常见的变量名词的概念
1.常见的名词叫法:全局变量、(局部变量、内部变量、自动变量)、静态变量、(成员变量、实例变量、对象类型变量、基础数据类型变量)、属性变量等。
2.常见变量名词之间的关系
- 全局变量:定义在函数外或大括号外声明定义的变量;存储在内存的全局(静态)存储区,序启动时就会分配存储空间,直到程序结束才会释放。
- 局部变量:需要在函数内部声明,只能被内部引用。也叫内部变量,也叫自动变量。存储在栈区stack,系统会自动释放。
- 静态变量:可以声明在函数外(静态全局变量),也可声明在函数或代码块内部(静态局部变量),当声明在函数外部时就是使用static关键字。存储在全局(静态)存储区,其内存会直到程序结束才会被销毁,即生命周期是整个程序。
- 成员变量:包括了实例变量(即对象类型变量)和基础数据类型变量以及常量,比如NSString为常量类型,属于成员变量。成员变量存储在堆中(当前对象对应的堆得存储空间中),不会被系统自动释放,只能有程序员手动释放。
- 实例变量:也叫对象类型变量,是一种特殊的成员变量,它为对象类型,例如NSObject类型。
- 属性变量:在OC中用@property来声明一个属性,编译器会自动为实例变量生产setter和getter方法。声明属性时会默认创建格式为“_className”的成员变量。
其他说明:成员变量存储在ro的ivars里面;属性变量存储在rw的property_array_t里面;相关链接:https://www.jianshu.com/p/6b1fb74bb9c5
3.常见变量的理解
1.成员变量 = 实例变量(对象类型变量) + 基础数据类型变量
成员变量中包括:实例变量(对象类型变量)和基础数据类型变量,实例变量是由类定义声明的对象。
1.在{}中声明的都是成员变量 ;
2.实例变量本质上也是成员变量,只是实例是针对类而言,实例是指类的声明,也就是如果成员变量的类型是一个类则称这个变量为实例变量;
3.成员变量用于类内部,无需与外界接触的变量。因为成员变量不会生成setter、getter方法,所以外界无法与成员变量接触;
4.成员变量创建后默认是@protected类型,就是受保护的,外接无法访问。
5.成员变量存储在堆中(当前对象对应的堆存储空间中),不会被系统自动释放,只能有程序员手动释放。
举例1:
.h文件中
@interface Person : NSObject {
//声明成员变量,默认访问权限修饰词是protected
int age;//基础数据类型变量(成员变量)
UIButton *btn;//实例变量
NSString *string;//实例变量
id className;//id是OC特有的类,本质上讲id等同于(void *),所以id data属于实例变量。
}
.m文件中
#import "Persion.h"
@interface Persion () {
int height;//成员变量
NSString *gender;//实例变量
}
@end
举例2:
#import "ViewController.h"
#import "Test.h"
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Test *test = [[Test alloc] init];
//当Test的country成员变量访问权限未public时,此时可以通过->访问country
NSLog(@"%@",test->country);
}
@end
//Test类
#import <Foundation/Foundation.h>
@interface Test : NSObject
{
@public
NSString *name;
NSString *city;
NSString *country;
int age;
}
@end
注意:不建议使用test->country这种方式来访问使用@public修饰的成员变量,因为这样是不安全的,所以建议手动添加自定义成员变量的get/set方法。
//Test类
#import <Foundation/Foundation.h>
@interface Test : NSObject
{
@public
NSString *name;
NSString *city;
NSString *country;
int age;
}
- (NSString *)getName;
- (void)setName:(NSString *)name;
@end
//test.m中
- (NSString *)getName {
return name;
}
- (void)setName:(NSString *)newName {
name = newName;
}
2.属性变量
1.通常我们使用@property声明的变量都叫做属性变量;当我们创建一个属性变量时,系统会自动创建一个与之对应的成员变量。
2.属性变量系统会自动创建对应的 getter 和 setter 方法。
3.声明在.文h件时,在引用当前类的的文件中可以访问;在.m 中创建的属性变量只能在类内部使用,外部无法使用。
4.声明属性时会默认创建格式为“_className”的成员变量;属性简化了我们为成员变量实现set/get方法的操作,属性专用于从类外部对变量进行调用和赋值。
5.因为有创建的getter setter 方法,则可以直接使用点语法来使用属性变量。举例:当我们创建一个属性变量hobby后,就可以直接使用成员变量_hobby进行赋值,可见系统已经创建了对应的成员变量。
.h文件
#import <Foundation/Foundation.h>
@interface Persion : NSObject
@property (nonatomic, copy) NSString *info;//属性变量
@end
.m文件
#import "Persion.h"
@interface Persion ()
@property (nonatomic, copy) NSString *hobby;//属性变量
@end
扩展说明:
1.@synthesize和@dynamic
@synthesize:表示告诉编译器自动实现成员变量的getter/setter方法,并将存取方法作用于系统根据属性名创建的“_className”变量。
@dynamic:表示告诉编译器不需要自动生产getter/setter方法,由开发者手动实现getter/setter。
2.@property修饰符
原子性/非原子性:属性默认是atomic原子性的,表示只有一个线程访问变量,原子性影响效率;属性设置nonatomic可以使用多个线程访问变量,优点效率快,大部分情况下使用非原子性 nonatomic。
读写属性修饰符:readonly只读属性,只生成getter方法,不会生成setter方法;readwrite读写属性,生产getter和setter方法,属性默认readwrite。
设置setter/getter别名:getter = 设置getter新的别名;setter = 设置setter新的别名;
3.内存管理修饰关键词
- assign:
默认值,主要用于简单的基础数据类型,例如int、float等,用于直接赋值,不做任何内存管理。- strong:
在ARC环境下使用,表示强引用指针,相当于retain,其修饰的对象引用计数会增加1,该对象只要引用计数不为0则不会被销毁。- weak:
在ARC环境下使用,表示弱引用指针,weak修饰的对象在释放后,指针地址会被置为nil。- copy:
对原有对象进行拷贝,复制每次都会在内存中拷贝一份对象,指针指向不同地址,一般用在修饰有可变对应类型的不可变对象上,如NSString, NSArray, NSDictionary。- retain:
会自动帮我们生成getter/setter方法内存管理的代码,在setter方法中,对传入的对象进行引用计数加1的操作,一般用于对象的修饰。
3.全局变量
全局变量:定义在函数外或大括号外声明定义的变量,全局变量存储在内存的全局(静态)存储区,程序启动时就会分配存储空间,直到程序结束才会被释放销毁。
1.全局变量的声明位置在OC中可以声明在.h或.m文件的函数外部。
2.全局变量可以跨文件访问,其作用域是整个源程序。
3.可以在声明时赋上初始值,如果没有赋初始值,系统自动赋值为0。
3.存储位置:既非堆,也非栈,而是专门的全局(静态)存储区static。
4.要调用其他文件中的全局变量使用extern声明下。
5.要防止其他文件调用本文件的全局变量要在本文件的全局变量的声明的数据类型之前加上static关键字。
6.我们可以使用extern关键字来引用某个全局变量,如 extern NSString *rAPPName; 将全局变量使用 extern 关键字在.h文件中声明,并在.m文件中实现赋值,使用时导入.h文件就能直接使用。
举例1
float lastNum;//仅声明
float lastNum = 10.0;//声明和定义
举例2:全局变量使用extern
1.Global.h
类的头文件中声明这个全局变量字符串
#import <Foundation/Foundation.h>
extern NSString *globalStr;
@interface Constants : NSObject {
}
@end
2.Global.m
#import "Constants.h"
NSString *globalStr= @"自定义";//在这里给globalStr赋初始化值
或者 NSString *globalSt;//这里声明和.h声明的全局变量相同,下面方法中赋值
@implementation Constants
- (instancetype)init {
globalStr = @"自定义全局变量";//这里可以直接赋值了
}
@end
3.BasicDemosViewController中使用
#import "BasicDemosViewController.h"
#import "Globals.h"//引入Global.h头文件
@implementation BasicDemosViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"%@",globalStr);
}
举例3:bbx项目中全局变量使用extern
静态全局变量:在局部变量的说明前再加上static说明符就构成静态局部变量;其生命周期没有变,依旧是在程序结束时才销毁。但是其作用域变了,现在只限于申明它的这个文件才可见。
4.局部变量(内部变量/自动变量)
局部变量也称作内部变量和自动变量,需要在函数内部声明定义,在内存中存储在栈区stack,仅在本文件本函数内可访问。当函数结束后释放内存,局部变量也会被销毁。包含了普通局部变量和静态局部变量(用static修饰局部变量)。
普通局部变量:
普通局部变量在函数/方法/代码块内声明的变量。变量存储在栈区(stack),它的生命周期、作用域都是在这个代码块内。一旦出了这个代码块,存储局部变量的这个栈内存就会被回收,局部变量也就被销毁。静态局部变量(用static修饰局部变量):
静态局部变量和全局变量、静态全局变量一样,是存储在‘静态存储区’。该局部变量只初始化一次,在内存中的地址不变,静态局部变量的生命周期是整个源程序,但作用域是声明它的代码块内。退出该函数后,尽管该变量还继续存在,但不能使用它。注意:编译器采用就近原则,当有两个变量的名称相同时(一个全局变量一个局部变量)取最接近的变量进行处理。即当全局变量与局部变量冲突时,全局变量服从局部变量。
-(void)viewDidLoad {
//局部变量
NSArray *array = [[NSArray alloc] initWithObject:@“123”, nil];
}
5.静态变量(包含:静态全局变量 + 静态局部变量)
静态变量可以声明在函数外,也可声明在函数或代码块内部(即可修饰原全局变量亦可修饰原局部变量)。
- 静态全局变量:当声明在函数外部时就是使用static关键字将全局变量变为静态全局变量;改变了全局变量的作用域:只限于声明静态全局变量的整个文件访问使用。
- 静态局部变量:当声明在函数或代码块内部时,被称为静态局部变量;其存储区域也是在全局(静态)存储区,静态局部变量的生命周期是整个程序源,但它的作用域只在代码块内。
注意:
1.存储在静态存储区,其内存会直到程序结束才会被销毁,即生命周期是整个程序;
2.当静态全局变量声明在.h头文件时,其他类引用静态全局变量所在类头文件后可以访问;
3.可以在声明时赋上初始值,如果没有赋初始值,系统自动赋值为0;
4.仅声明该变量的文件可以访问;
5.存储位置:既非堆,也非栈,而是专门的全局(静态)存储区static;
6.把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。
static float lastNum;
static float lastNum = 10.0;
扩展:iOS之const、extern、static用法
https://www.jianshu.com/p/0321e121214e
https://blog.csdn.net/winzlee/article/details/50790571
6.内存分区
栈区(stack):由编译器自动分配和释放,存放函数的参数值,局部变量的值等。操作方式类似于数据结构中的栈。
堆区(heap):一般由程序员分配和释放,若程序员不释放,程序结束时可能由操作系统回收。与数据结构中的堆是两码事,分配方式类似于链表。
全局区(静态区)(static):全局变量和静态变量存放在此。
文字常量区:常量字符串放在此,程序结束后由系统释放。
程序代码区:存放函数体的二进制代码。