去阿里面试被问到了好多基本功和底层知识,由于没有总结过,导致有很多用过的东西并不知道怎么表达,遭到了无情的鄙视;决定要每周总结一个知识点,立下flag,激励自己一下!
先从最基本的写起:先写几个修饰关键字 const、static、extern.
一、const:: 被const修饰的变量是只读的(变量->只读变量).
先从最基础的开始
定义变量
int a = 1;
a = 20;// 允许修改值
然后开始进入const
const两种用法
1、const:修饰基本变量p
const int b = 20; // b:只读变量
int const b = 20; // b:只读变量
b = 1; // 会报错// 不允许修改值
2、const:修饰指针类型*p
定义一个指向int类型的指针,指向a的地址
int a = 20;
int *p = &a;
NSLog(@"p == %p---&a == %p",p,&a);
int c = 10;
p = &c;
NSLog(@"p == %p---*p == %d",p,*p);
int d = 11;
*p = d;
NSLog(@"p == %p---*p == %d",p,*p);
//打印结果
2019-07-11 09:45:32.867368+0800 Runtime理解[44662:1691119] p == 0x7ffeefb433ac---*p == 20
2019-07-11 09:45:32.867518+0800 Runtime理解[44662:1691119] p == 0x7ffeefb4339c---*p == 10
// 允许修改p指向的地址
2019-07-11 09:45:32.867637+0800 Runtime理解[44662:1691119] p == 0x7ffeefb4339c---*p == 11
// 允许修改p访问内存空间的值
const修饰指针变量访问的内存空间,修饰的是右边*p1,
int a = 20;
const int *p = &a;
NSLog(@"p == %p---*p == %d",p,*p);
int c =10;
p = &c;
NSLog(@"p == %p---*p == %d",p,*p);
//打印结果
2019-07-11 10:15:54.968002+0800 Runtime理解[45151:1709783] p == 0x7ffee3f193ac---*p == 20
2019-07-11 10:15:54.968132+0800 Runtime理解[45151:1709783] p == 0x7ffee3f1939c---*p == 10
// 允许修改p指向的地址
int d = 11;
*p = d;//报错,Read-only variable is not assignable(只读变量不可赋值)
// 不允许修改p访问内存空间的值
int a = 20;
const *p = &a;
NSLog(@"p == %p---*p == %d",p,*p);
int c =10;
p = &c;
NSLog(@"p == %p---*p == %d",p,*p);
//打印结果
2019-07-11 10:15:54.968002+0800 Runtime理解[45151:1709783] p == 0x7ffee3f193ac---*p == 20
2019-07-11 10:15:54.968132+0800 Runtime理解[45151:1709783] p == 0x7ffee3f1939c---*p == 10
// 允许修改p指向的地址
int d = 11;
*p = d;//报错,Read-only variable is not assignable(只读变量不可赋值)
// 不允许修改p访问内存空间的值
int a = 20;
int const *p = &a;
NSLog(@"p == %p---*p == %d",p,*p);
int c =10;
p = &c;
NSLog(@"p == %p---*p == %d",p,*p);
//打印结果
2019-07-11 10:15:54.968002+0800 Runtime理解[45151:1709783] p == 0x7ffee3f193ac---*p == 20
2019-07-11 10:15:54.968132+0800 Runtime理解[45151:1709783] p == 0x7ffee3f1939c---*p == 10
// 允许修改p指向的地址
int d = 11;
*p = d;//报错,Read-only variable is not assignable(只读变量不可赋值)
// 不允许修改p访问内存空间的值
总结:const int *p 作用等同于 int const *p
*p 是常量 p是变量
int a = 20;
int * const p = &a;
NSLog(@"p == %p---*p == %d",p,*p);
int c =10;
p = &c; //报错,Read-only variable is not assignable(只读变量不可赋值)
// 不允许修改p指向的地址
int d = 11;
*p = d;
NSLog(@"p == %p---*p == %d",p,*p);
// 打印结果
2019-07-11 10:50:44.694506+0800 Runtime理解[45665:1731013] p == 0x7ffee8f413ac---*p == 20
2019-07-11 10:50:44.694684+0800 Runtime理解[45665:1731013] p == 0x7ffee8f413ac---*p == 11
// 允许修改p访问内存空间的值
总结: int *const p *p是变量 p是常量;
通过以上代码总结:const修饰的紧右边的为常量,在此就不一一验证,有兴趣的可以自己写代码验证一下;
const int *p // *p:常量 p:变量
int const *p // *p:常量 p:变量
int *const p // *p:变量 p:常量
const int * const p // *p:常量 p:常量
int const * const p // *p:常量 p:常量
下面来试验一下const最常用的方法:修饰NSString,废话少说,上代码:
NSString * const str1=@"sdh";
str1 = @"123" //报错
//因为NSString是不可变字符串:指针指向内存的内容是不允许改变的,因为const修饰的是指针 str1:指针不允许指向其他内存;
//所以不可以修改指针指向的原内存中的内容,指针不可以指向其他的内存
NSString const *str2 = @"sdh";
str2 = @"123"; //不报错
//因为NSString是不可变字符串:指针指向内存的内容是不允许改变的,因为const修饰的是指针 *str2:指针指向内存的内容不允许改变,所以 *str2 是常量 但str2是变量指针可以指向一天内存;
//所以不可以修改指针指向的原内存中的内容,指针可以指向其他的内存
NSMutableString *const var1 =[NSMutableString string];
NSMutableString *var2 = [NSMutableString stringWithFormat:@"%@",@"123"];
var1 = var2; //报错 因为const修饰的是指针 var1:指针不允许指向其他内存;
[var1 appendString:@"123"]; //不报错 因为NSMutableString是可变字符串:指针指向内存的内容是允许改变的
//所以可以修改指针指向的原内存中的内容,指针不可以指向其他的内存
NSMutableString const *var3 =[NSMutableString string];
var3 = var2; //不报错 因为const修饰的是指针 *var3 所以var3是变量,指针可以指向其他内存地址
[var3 appendString:@"123456"]; //不报错 因为var3是可变字符串指针指向的原内存中的内容可以改变
//所以可以修改指针指向的原内存中的内容,常量指针可以指向其他的内存
NSLog(@"str1 = %@ -- str2 = %@ -- var1 = %@ -- var2 = %@",str1,str2,var1,var2);
//打印结果
2019-07-11 11:28:43.957979+0800 Runtime理解[46292:1755776] str1 = sdh -- str2 = 123 -- var1 = 123 -- var2 = sdh123456
const修饰字符串总结:
NSString *const str1
不可以修改指针指向的原内存中的内容,指针不可以指向其他的内存
NSString const *str2
不可以修改指针指向的原内存中的内容,指针可以指向其他的内存
NSMutableString const *var1
可以修改指针指向的原内存中的内容,指针可以指向其他的内存
NSMutableString *const var2
可以修改指针指向的原内存中的内容,指针不可以指向其他的内存
const用法总结道这里。
二、static和extern简单使用
"static作用":
1、修饰局部变量:
1)延长局部变量的生命周期,程序结束才会销毁。
2)局部变量只会生成一份内存,只会初始化一次。
3)Static关键字不可以改变局部变量的作用域。
@implementation Person
-(instancetype)init{
self = [super init];
if (self) {
[self conlogAge];
[self conlogAge];
}
return self;
}
-(void)conlogAge{
static NSInteger age = 18;
age ++;
NSLog(@"%ld",age);
}
#import "Person.h"
@interface ConstStaticViewController ()
@end
@implementation ConstStaticViewController
- (void)viewDidLoad {
[super viewDidLoad];
Person *per = [Person new];
Person *per2 = [Person new];
}
//打印输出
//2019-07-11 15:19:25.617612+0800 Runtime理解[49598:1888382] 19
//2019-07-11 15:19:25.617712+0800 Runtime理解[49598:1888382] 20
//2019-07-11 15:19:25.617800+0800 Runtime理解[49598:1888382] 21
//2019-07-11 15:19:25.617892+0800 Runtime理解[49598:1888382] 22
注意:ConstStaticViewController是二级页面,我们返回到一级页面,该页面销毁,在重新创建改页面,viewDidLoad会重新执行一次,我们在看打印数据:
2019-07-11 15:42:11.181260+0800 Runtime理解[50022:1905671] 19
2019-07-11 15:42:11.181433+0800 Runtime理解[50022:1905671] 20
2019-07-11 15:42:11.181541+0800 Runtime理解[50022:1905671] 21
2019-07-11 15:42:11.181711+0800 Runtime理解[50022:1905671] 22
2019-07-11 15:42:14.761006+0800 Runtime理解[50022:1905671] 23
2019-07-11 15:42:14.761159+0800 Runtime理解[50022:1905671] 24
2019-07-11 15:42:14.761285+0800 Runtime理解[50022:1905671] 25
2019-07-11 15:42:14.761378+0800 Runtime理解[50022:1905671] 26
说明:
1、age这个变量并未因持有他的对象的销毁儿销毁,局部变量的生命周期被延长
2、而且, [self conlogAge]函数被调用多次,age这个变量并未重新初始化,而是跳过初始化,继续进行 ++操作,所以:局部变量只会生成一份内存,只会初始化一次。
-(void)conlogAge{
static NSInteger age = 18;
age ++;
NSLog(@"%ld",age);
}
-(void)showAge{
age++;//直接报错
extern NSInteger age; //编译时报错
NSLog(@"%ld",age);
}
3、被static修饰过的变量,在其他作用域是不能访问的。
2、static修饰全局变量
1)只能在本文件中访问,修改全局变量的作用域,生命周期不会改
2)避免重复定义全局变量
NSString *allChange = @"allChange";//全局变量,本文件直接用,其他文件借助extern调用
static NSString *changes =@"change";//全局变量,本文件直接用,其他文件借助不能调用,用extern调用,会报错
三、const、static配合使用
开发时常用的写法,用const、和static配合使用来代替宏的部分功能(#define)
一、const与宏的区别(面试题):
- "const简介":之前常用的字符串常量,一般是抽成宏,但是苹果不推荐我们抽成宏,推荐我们使用const常量。
- "执行时刻":宏是预编译(编译之前处理),const是编译阶段。
- "编译检查":宏不做检查,不会报编译错误,只是替换,const会编译检查,会报编译错误。
- "宏的好处":宏能定义一些函数,方法。 const不能。
- "宏的坏处":使用大量宏,容易造成编译时间久,每次都需要重新替换。
#define CHANGE @"change";
static NSString *const changes =@"change";
虽然测试过,及时用相当数量的宏编译时间并不会差很多,但是,我们还是要按照苹果的推荐来的好,用第二个表示方法。
四、extern使用
"extern作用":只是用来获取全局变量(包括全局静态变量)的值,不能用于定义变量。
"extern工作原理":先在当前文件查找有没有全局变量,没有找到,才会去其他文件查找。
废话补多说:代码解释
#import "SLStaticDemo.h"
NSInteger age = 10;
@implementation SLStaticDemo
@end
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
extern NSInteger age;
NSLog(@"age = %ld", (long)age);
age += 10;
NSLog(@"age = %ld", (long)age);
}
@end
打印结果:
从输出结果 age = 10 我们可以看到即便我们在ViewController.m中并没有import SLStaticDemo.h也能得到SLStaticDemo中定义的age变量,这就是extern关键字的功劳(extern可以访问全局变量);
注:这里理解起来很简单,懒得自己写了,参照的大神的代码,网址:https://www.jianshu.com/p/9c09989b6862
好,用了将近一天的时间,把这三个基本的关键字理清了,可能有写啰嗦,希望大家选择性参考。
网友评论