ios面试题总结

article/2025/8/20 1:32:15

本篇主要针对面试题进行解析,会进行基础知识的总结和拓展,仅供参考,如有错误,欢迎指出,一起学习!

一、关于Foundation框架中的问题

(一)NSCache & NSDictionary

1.NSDictionary(字典)

字典是由键-值(key-value)组成的数据集合,其中值为对象,可以通过键从字典中获取需要的值。字典中的键必须唯一,通常情况下,键为字符串对象,主要用于注明存储对象的说明,但键也可以是其他类型的对象,和键相关联的值可以是任何类型的对象,但是不能是nil。

关于字典的思维导图

​​​​​​​ 

不可变字典(NSDictionary)

不可变字典一旦创建好之后,不能在增加/删除/修改键值对

创建不可变字典的几种方式:

方式一:打印结果不一定按顺序排列

    NSDictionary *dict = @{@"website":@"www.99ios.com",@"name":@"九九学院",@"business":@"ios学习",@"founderYear":@2016,};NSLog(@"第一种类型创建的字典为:%@",dict);

打印出来的结果为:

方式二:注意其顺序,是value,key,并且以nil结尾,打印顺序是从后往前

NSDictionary *dict1 = [NSDictionary dictionaryWithObjectsAndKeys:@"www.99ios.com",@"website",@"九九学院",@"name", nil];NSLog(@"第二种类型创建的字典为:%@",dict1);

打印结果为:

方式三:创建一个key的数组,创建一个value的数组,打印顺序也是从后往前

 NSArray *keys = @[@"website",@"name"];NSArray *values = @[@"www.99ios.com",@"九九学院"];NSDictionary *dict2 = [NSDictionary dictionaryWithObjects:values forKeys:keys];NSLog(@"第三种类型创建的字典为:%@",dict2);

 打印结果:

从字典中获取键值:

 使用dict[key]或者是objectForKey

    //访问键值的两种方式NSString *website = dict[@"website"];NSLog(@"website的值是:%@",website);NSString *name = [dict objectForKey:@"name"];NSLog(@"name的值是:%@",name);NSNumber *num = dict[@"founderYear"];NSLog(@"founderYear的值是%@",num);

打印结果为:


​​​​​​​

遍历字典中的键值对:

    //遍历字典中的键值对for (NSDictionary *key in dict) {NSLog(@"key:%@,value:%@",key,dict[key]);}

打印的结果为:

获取键值对的数量:

    //获取键值对的数量NSUInteger count = dict.count;NSLog(@"dict中的键值对数量为:%lu",(unsigned long)count);

 打印的结果为:

获取字典中所有的键:

    //获取一个字典中所有的键NSArray *allKeyArrays = dict.allKeys;NSLog(@"dict中所有的键为:%@",allKeyArrays);

 打印结果为:

获取字典中所有的值: 

    //获取一个字典中所有的值NSArray *allValuesArrays = dict.allValues;NSLog(@"dict中所有的值为:%@",allValuesArrays);

打印结果为:


​​​​​​​

可变字典(NSMutableDictionary)

可变字典是不可变字典的子类,NSMutableDictionary继承了NSDictionary类的属性和方法,其键值对可以增加、修改和删除

对可变字典进行初始化:

    //创建可变字典的3种方式NSMutableDictionary *mutableDict = [NSMutableDictionary dictionary];NSMutableDictionary *mutableDict1 = [NSMutableDictionary dictionaryWithCapacity:100];NSMutableDictionary *mutableDict2 = [NSMutableDictionary dictionary];[mutableDict2 initWithContentsOfFile:@"path"];

增加键值对:

    //增加键值对[mutableDict setObject:@"www.99ios.com" forKey:@"website"];[mutableDict setObject:@"九九学院" forKey:@"name"];NSLog(@"website:%@",mutableDict[@"website"]);NSLog(@"name:%@",mutableDict[@"name"]);

打印结果为:

 修改键值对的值:只需要拿到值,进行重新赋值就可以

    //修改键值对的值mutableDict[@"website"] = @"www.apple.com";mutableDict[@"name"] = @"苹果公司";NSLog(@"新的website的值为:%@",mutableDict[@"website"]);NSLog(@"新的name的值为:%@",mutableDict[@"name"]);

​​​​​​​

 移除字典中某一个键值对:

    //移除键值对中的值[mutableDict removeObjectForKey:@"website"];NSLog(@"移除website之后的可变字典为:%@",mutableDict);

打印结果为:

移除多个键值对,需要将移除的key存放在一个数组中

    //移除多个键值对,把移除的所有的键存放在一个数组中NSArray *removeDicts = @[@"website",@"name"];[mutableDict removeObjectsForKeys:removeDicts];NSLog(@"移除website和name之后的可变字典为:%@",mutableDict);

 打印结果为:

   

 移除所有键值对:

    //移除所有键值对[mutableDict removeAllObjects];NSLog(@"移除所有键值对后的字典为:%@",mutableDict);

 打印结果为:

二、分类 

1.Category的实现原理,以及Category为什么只能加方法不能加属性

2.Category中有load方法吗?load方法是什么时候调用的?load方法能继承吗?

3.load、initialize在category中调用的顺序,以及出现继承时他们之间的调用过程?

4.load、initalize的区别,以及他们在category重写的时候调用的次序?

三、对象的本质

1.一个NSObject对象占用多少内存?​​​​​​​

答:系统分配了16个字节给NSObject,但NSObject对象只使用了8个字节(64位环境)​​​​​​​

9. 复杂的继承的内存空间占用

@interface Person : NSObject{@publicint _age;
}
@end@implementation Person@end
@interface Student : Person
{int _no;
}@end@implementation Student@end

内存对齐原则:

结构体的大小必须是最大成员大小的倍数 

10.定义属性的本质

我们创建出来的实例对象,他的内存里面不存在方法,只存有成员变量和isa指针,为什么实例对象里面不包含方法,因为每一个实例对象都可以为他的属性赋自己固定的值,但是方法只需要都是通用的,只需要调用1次

@interface Person : NSObject{@publicint _age;
}@property (nonatomic, assign) int height;@end

定义一个属性的本质是添加一个_的成员变量,同时生成get和set方法

struct Person_IMPL {struct NSObject_IMPL NSObject_IVARS;int _age;int _height;
};// @property (nonatomic, assign) int height;
/* @end */// @implementation Personstatic int _I_Person_height(Person * self, SEL _cmd) { return (*(int *)((char *)self + OBJC_IVAR_$_Person$_height)); }
static void _I_Person_setHeight_(Person * self, SEL _cmd, int height) { (*(int *)((char *)self + OBJC_IVAR_$_Person$_height)) = height; }

2.对象的isa指针指向哪里?

1)OC对象的分类

OC中的对象主要分为3种

a.instance对象(实例对象)

instance对象就是通过类alloc出来的对象,每次调用alloc产生新的instance对象

    NSObject *object1 = [[NSObject alloc]init];NSObject *object2 = [[NSObject alloc]init];

object1、object2是NSObject的instance对象(实例对象)

他们是不同的两个对象,分别占据着两块不同的内容

instance对象在内存中存储的信息包括:isa指针和成员变量的值

isa指针的内存地址,就是Person对象的内存地址

    //实例对象MJPerson *person = [[MJPerson alloc]init];person->_age = 10;MJPerson *person2 = [[MJPerson alloc]init];person2 -> _age = 20;NSLog(@"person对象的地址是:%p,person2对象的地址是:%p",person,person2);

b.class对象 (类对象)

一个类的类对象是唯一的,一个类的类对象在内存中只有一份,有3种方式可以得到类对象,将他们的地址打印是同一个类对象

    //类对象
//    1)通过实例对象调用class方法  一个类的类对象在内存中只有一份,一个类的类对象是唯一的Class personClass = [person class];Class personCLass3 = [person2 class];//2)通过类直接调用class方法Class personClass1 = [MJPerson class];//3)通过object_getClass方法Class personClass2 = object_getClass(person);Class personClass4 = object_getClass(person2);NSLog(@"MJPerson的类对象的地址是:%p  %p  %p  %p   %p",personClass,personCLass3,personClass1,personClass2,personClass4);

class对象在内存中存储的主要信息包括:

isa指针    superclass指针  类的属性信息(@property) 类的对象方法信息(instance method) 

类的协议信息(protocal)         类的成员变量信息(ivar) (指的是类型和名称,不是值)

c.meta-class对象 (元类对象)

元类对象  将类对象传入获取到元类对象 需要注意的是获取元类对象不能使用类对象调用class放大,class方法始终获取到的都是类对象,因为类对象只有1个,所以传入类对象进去,获取到的元类对象也只有1个

每个类在内存中有且只有一个meta-class对象

meta-class对象和class对象的内存结构是一样的,但是用途是不一样的,在内存中存储的信息主要包括:

isa指针   superclass指针  类的类方法信息(class method)

  Class personMetaClass = object_getClass([MJPerson class]);Class personMetaClass1 = object_getClass(personClass);NSLog(@"personMetaClass的内存地址是:%p  personMetaClass1的内存地址是%p",personMetaClass,personMetaClass1);


​​​​​​​

 2)isa指针指向哪里?

    //实例对象MJPerson *person = [[MJPerson alloc]init];//调用实例方法[person personInstanceMethod];//这个方法调用的本质是
//    objc_msgSend(person,@selector(personInstanceMethod));
//    objc_msgSend();//调用类方法[MJPerson personClassMethod];//类方法调用的本质是
//    objc_msgSend(MJPerson,@selector(personClassMethod));

调用对象方法的本质是向实例对象发送一个消息去调用对象方法

调用类方法的本质是向类对象发送一个消息去调用类方法

实例对象的isa指向类对象,类对象的isa指向元类对象

当调用对象方法时,通过instance的isa找到class,最后找到对象方法的实现进行调用

当调用类方法时,通过class的isa找到meta-class,最后找到类方法的实现并调用

3.OC的类信息存放在哪里?

四、KVO

1.ios用什么方式实现对一个对象的KVO?(KVO的本质是什么,KVO的内部实现原理)

答:当一个对象使用了KVO监听,ios系统会修改这个对象的isa指针,改为指向一个全新的通过Runtime动态创建的子类,子类拥有自己的set方法实现,内部会调用willChangeValueForKey,原来的set方法和didChangeValueForKey方法,didChangeValueForKey这个方法内部会调用监听器的监听方法

1)KVO全称是Key-Value Observing ,俗称“键值监听",可以用于监听某个对象属性值的改变

ViewController类


#import "ViewController.h"
#import "MJPerson.h"
#import "MJStudent.h"@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view.MJPerson *p1 = [[MJPerson alloc]init];p1.age = 1;MJPerson *p2 = [[MJPerson alloc]init];p2.age = 2;p1.age = 10;NSKeyValueObservingOptions options= NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;[p1 addObserver:self forKeyPath:@"age" options:options context:@"123"];[p2 addObserver:self forKeyPath:@"age" options:options context:@"456"];p1.age = 10;p2.age = 20;MJStudent *student = [[MJStudent alloc]init];[student changeAge];[p1 removeObserver:self forKeyPath:@"age"];[p2 removeObserver:self forKeyPath:@"age"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {NSLog(@"监听到%@的%@属性值改变了----%@",object,keyPath,change);
}@end

MJPerson类

.h文件
#import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGIN@interface MJPerson : NSObject@property (nonatomic, assign) int age;@endNS_ASSUME_NONNULL_END.m文件
#import "MJPerson.h"@implementation MJPerson@end

MJStudent

.h文件
#import <Foundation/Foundation.h>
#import "MJPerson.h"NS_ASSUME_NONNULL_BEGIN@interface MJStudent : NSObject@property (nonatomic, strong) MJPerson *person;- (void)changeAge;@endNS_ASSUME_NONNULL_END.m文件
#import "MJStudent.h"@implementation MJStudent- (void)changeAge {self.person = [[MJPerson alloc]init];self.person.age = 30;
}@end

打印的结果为:

需要注意的是监听某个对象的属性的值,像上面在MJStudent里面创建了一个MJPerson的对象,他和p1和p2对象是不同的对象,所以监听不到他的属性值的改变。还有就是需要在改变他的值之前给他添加监听,如果要是在修改之后监听的话也监听不到他的属性值的改变。 

2)KVO的本质

给属性进行赋值实际上就是调用了该对象的set方法

    person1.age = 10; //[person1 setAge:10];
@interface MJPerson : NSObject@property (nonatomic ,assign) int age;@end@implementation MJPerson- (void)setAge:(int)age {_age = age;
}@end

实例对象的isa指针指向类对象,当person1的属性值还没有发生改变的时候,会发现它的isa指针指向他的类对象,当他的isa指针发生改变的时候,会指向一个新的类对象,这个类对象是NSKVONotifying_MJPerson

没有使用KVO监听的对象

会直接调用MJPerson的setAge方法

使用了KVO监听的对象,会在运行时产生一个新的类 NSKVONotifying_MJPerson,他是MJPerson的一个子类

- (void)setAge:(int)age {__NSSetIntValueAndNotify();}void __NSSetIntValueAndNotify(......) {[self willChangeValueForKey:@"age"];[super setAge:age];[self didChangeValueForKey:@"age"];
}- (void)didChangeValueForKey:(NSString *)key {[observer observeValueForKeyPath:@"age" ofObject:self change:@{} context:nil];
}

使用了KVO监听的对象,系统会在运行时产生一个新的类,这个类有一个setAge的方法,setAge方法会调用C语言的__NSSetIntValueAndNotify()的方法,这个方法里面会调用willChangeValueForKey方法,然后调用父类的setAge方法,然后调用didChangeValue方法,didChangeValue方法里面调用了observeValueForKeyPath的方法,所以能够监听到他的值的变化

3)验证isa指针和IMP实现

a.可以使用系统的runtime函数去获取到类对象

  NSLog(@"p1监听KVO之前的--p1的Class%@ --p2的Class%@",object_getClass(person1),object_getClass(person2));NSLog(@"p1监听KVO之后的--p1的Class%@ --p2的Class%@",object_getClass(person1),object_getClass(person2));

 

 b.打印出对象的方法的地址,然后在控制台看他的方法实现

NSLog(@"p1监听KVO之前的-----%p,%p",[person1 methodForSelector:@selector(setAge:)],[person2 methodForSelector:@selector(setAge:)]);NSLog(@"p1监听KVO之后的-----%p,%p",[person1 methodForSelector:@selector(setAge:)],[person2 methodForSelector:@selector(setAge:)]);

​​​​​​​

4)验证KVO底层原理的实现

MJPerson类里面添加如下的3个方法

//
//  MJPerson.m
//  KVO的基本使用
//
//  Created by Hanvon on 2023/2/16.
//#import "MJPerson.h"@implementation MJPerson- (void)setAge:(int)age {NSLog(@"调用setAge方法");_age = age;
}- (void)willChangeValueForKey:(NSString *)key {NSLog(@"willChangeValue--begin");[super willChangeValueForKey:key];NSLog(@"willChangeValue--end");
}- (void)didChangeValueForKey:(NSString *)key {NSLog(@"didChangeValue--begin");[super didChangeValueForKey:key];NSLog(@"didChangeValue--end");
}//- (int)age {
//    return _age;
//}
@end

修改属性值,各个方法的调用时机如下

会发现确实在didChangeValueForKey方法里面进行的observer方法的调用

2.如何手动触发KVO(如何手动触发一个value的KVO,如何关闭默认的KVO实现,并进入自定义的KVO实现)​​​​​​​

答:手动调用willChangeValueForKey和didChangeValueForKey方法

    [p1 willChangeValueForKey:@"age"];[p1 didChangeValueForKey:@"age"];

只需要调用这两个方法,不需要设置属性值的改变

五、性能优化

1.你在项目中是怎么优化内存的?

(一)卡顿优化

1)在屏幕成像的过程中,CPU和GPU起着至关重要的作用

CPU(Center Processing Unit,中央处理器),对象的创建和销毁,对象属性的调整、布局计算、文本的计算和排版、图片的格式转换和解码,图像的绘制

CPU(Graphics Processing Unit,图像处理器)纹理的渲染

 

在ios中是双缓冲机制,有前帧缓存、后帧缓存。

2)图像的成像原理

会先发一个垂直同步信号,然后这一屏幕上发水平同步信号,等到一屏幕铺满,再发垂直同步信号

3)造成卡顿的原因

当CPU计算完毕之后,GPU进行渲染,然后发送垂直同步信号,如果GPU渲染时间过长,垂直信号就会将上一帧的内容进行显示,俗称掉帧,这一帧的内容就会在下一次垂直信号发出的时候显示,就造成了卡顿

4)卡顿解决的主要思路

a.尽可能减少CPU、GPU资源消耗

b.按照60FPS的帧刷新率,每隔16ms就会有一次VSync信号发出。

c.平时所说的卡顿主要是因为在主线程中执行了比较耗时的操作,可以通过Observer到主线程RunLoop中,通过监听RunLoop切换的耗时,以达到监控卡顿的目的

5)CPU方面优化卡顿

a.尽量用轻量级的对象,比如用不到事件处理的地方,可以考虑使用CALayer取代UIView

b.不要频繁地调用UIView的属性,比如frame、bounds、transform等属性,尽量减少不必要的修改

c.尽量提前计算好布局,在有需要时一次性的调整对应的属性,不要多次的修改属性

self.view.frame = CGRectMake(self.view.frame.origin.x+1,self.view.frame.origin.y,self.view.frame.size.width,self.view.frame.size.height);

d.Autolayout会比直接设置frame消耗更多的CPU资源

e.图片的size最好刚好跟UIImageView的size保持一致

f.控制一下线程的最大并发数量

g.尽量把耗时的操作放到子线程(文本处理(尺寸计算、绘制),图片处理(解码、绘制))

文本处理:

  //文本计算[@"text" boundingRectWithSize:CGSizeMake(100, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:nil context:nil];//文本绘制[@"text" drawWithRect:CGRectMake(0, 0, 100, 200) options:NSStringDrawingUsesLineFragmentOrigin attributes:nil context:nil];

图片处理:

6)GPU方面优化卡顿

a.尽量避免短时间内大量图片展示,尽可能将多张图片合成一张进行显示

b.GPU能处理的最大纹理尺寸是4096x4096,一旦超过这个尺寸,就会占用GPU资源进行处理,所以纹理尽量不要超过这个尺寸

c.尽量减少视图数量和层次

d.减少透明的视图(alpha<1),不透明的设置成opaque为YES

e.尽量避免减少出现离屏渲染

7)离屏渲染

a.在OpenGL中,GPU有两种渲染方式:

On-Screen Rendering:当前屏幕渲染,在当前用于显示的屏幕缓冲区进行渲染操作

Off-Screen Rendering:离屏渲染,在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作

b.离屏渲染消耗性能的原因

需要创建新的缓冲区

离屏渲染的整个过程中,需要多次切换上下文,先是从当前屏幕(On-Screen)切换到离屏(Off-Screen),等到离屏渲染结束以后,将离屏缓冲区的渲染结果渲染到屏幕上,又需要将上下文环境从离屏切换到当前屏幕

c.哪些操作会触发离屏渲染?

光栅化:layer.shouldRasterize = YES

遮罩:layer.mask

圆角:同时设置layer.masksToBounds = YES   layer.cornerRadius = 0;(考虑通过CoreGraphics绘制裁剪圆角,或者叫美工提供圆角图片)

阴影:layer.shadowXXX(如果设置了layer.shadowPath就不会产生离屏渲染)

(二)耗电优化

1)耗电的主要来源

耗电的主要来源是CPU处理(Processing)、网络(NetWorking)、定位(Location)和图像(Graphics)

2)耗电优化:

CPU、GPU方面优化:

a.尽可能降低CPU、GPU的功耗

b.少用定时器

c.优化I/O操作

尽量不要频繁写入小数据,最好批量一次性写入

读写大量重要数据时,考虑用dispatch_io,其提供了基于GCD的同步操作文件I/O的API,用dispatch_io系统会优化磁盘访问

数据量比较大的,建议使用数据库(比如SQLite,CoreData)

网络优化:

a.减少、压缩网络质量

b.如果多次请求的结果是相同的、尽量使用缓存

c.使用断点续传,否则网络不稳定时可能多次传输相同的内容

d.网络不可用时,不要尝试执行网络请求

e.让用户可以取消长时间运行或者速度很慢的网络操作,设置合理的超时时间

f.批量传输,比如,下载视频流时,不要传输很小的数据包,直接下载整个文件或者一大块一大块的下载,如果下载广告,一次性多下载一些,然后再慢慢提示,如果下载电子邮件,一次下载多封,不要一封一封的下载

定位优化:

a.如果只是需要快速确定用户位置,最好用CLLocationManager的requestLocation方法,定位完成以后,会自动让定位硬件断电

b.如果不是导航应用,尽量不要实时更新位置,定位完毕就关闭定位服务

c.尽量降低定位精度,比如尽量不要使用精度最高的kCLLocationAccuracyBest

d.需要后台定位时,尽量设置pausesLocationUpdatesAutomatically为YES,如果用户不太可能移动的时候系统会自动暂停位置更新

(三)App启动优化

1)App的启动可以分为2种

冷启动(Cold Launch):从零开始启动App

热启动(Warm Launch):App已经存在在内存中,在后台存活着,再次点击图标启动App

2)App启动时间的优化,主要是针对冷启动进行优化

通过添加环境变量可以打印出App的启动时间分析(Edit scheme ->run -> Arguments)

DYLD_PRINT_STATISTICS设置为1

2.优化你是从哪几方面着手的?

3.列表卡顿的原因有哪些?你平时是怎么优化的?

4.遇到tableView卡顿嘛?会造成卡顿的原因大致有哪些?

六、多线程

1.你理解的多线程?

2.ios的多线程方案有哪几种?你更倾向于哪一种?

 

3.你在项目中使用过GCD吗?

1)GCD的常用函数

GCD有两个用来执行任务的函数

a.用同步的方式执行任务

dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);

queue:队列

block:任务

   dispatch_queue_t queue = dispatch_get_global_queue(0, 0);dispatch_sync(queue, ^{NSLog(@"执行任务~%@",[NSThread currentThread]);});

 同步是在当前线程执行任务

b.用异步的方式执行任务

dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

 dispatch_queue_t queue = dispatch_get_global_queue(0, 0);dispatch_async(queue, ^{NSLog(@"执行任务~%@",[NSThread currentThread]);});

异步是在子线程中处理任务

2)GCD的队列可以分为2大类型

并发队列:

可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)

    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);dispatch_async(queue, ^{for (int i = 0; i < 10; i++) {NSLog(@"执行任务1~%@",[NSThread currentThread]);}});dispatch_async(queue, ^{for (int i = 0; i < 10; i++) {NSLog(@"执行任务2~%@",[NSThread currentThread]);}});

 如果两个异步执行的并发队列,他们是随机进行的

全局队列是并发队列,并发队列只有在异步函数下生成才有效

串行队列:

让任务一个接着一个的执行(一个任务执行完毕以后,再执行下一个任务)

   //串行队列dispatch_queue_t queue1 = dispatch_queue_create("muqueue", DISPATCH_QUEUE_SERIAL);dispatch_async(queue1, ^{for (int i = 0; i < 10; i++) {NSLog(@"执行任务1~%@",[NSThread currentThread]);}});dispatch_async(queue1, ^{for (int i = 0; i < 10; i++) {NSLog(@"执行任务2~%@",[NSThread currentThread]);}});

会按顺序执行,执行完任务1,在执行任务2

所以只要是同步函数,只会在当前线程里面执行任务,就是按顺序来执行的

3)容易混淆的术语

有4个比较容易混淆的术语:同步,异步,并发,串行

同步和异步主要影响:能不能开启新的线程

同步:在当前线程中执行任务,不具备开启新线程的能力

异步:在新的线程中执行任务,具备开启新线程的能力

并发和串行的主要影响是:任务的执行方式

并发:多个任务并发(同时)进行

串行:一个任务执行完毕以后,再执行下一个任务

异步只是具备了开启新线程的能力,但是不一定开新的线程

 dispatch_queue_t mainQueue = dispatch_get_main_queue();//主队列dispatch_async(mainQueue, ^{NSLog(@"主线程执行任务3~%@",[NSThread currentThread]);});

如果是主队列的话,没有开启新的线程,是在主线程中执行

主队列是一个特殊的串行队列

4)各种队列的执行效果

​​​​​​​

5)面试题

a.以下代码会不会产生死锁(会)

- (void)viewDidLoad {[super viewDidLoad];NSLog(@"执行任务1");[self interview01];NSLog(@"执行任务3");
}- (void)interview01 {//以下代码是在主线程执行的,会不会产生死锁 会//队列:排队:FIFOdispatch_queue_t queue = dispatch_get_main_queue();dispatch_sync(queue, ^{NSLog(@"执行任务2");});
}

队列的特点是:FIFO,先进先出

dispatch_sync:立马在当前线程执行任务,执行完毕才能继续往下执行

队列是先进先出,会先执行viewDidLoad里面的任务, 所以会先执行任务1,当执行到sync函数的时候,会立即执行sync里面的任务,但是队列先进先出,viewDidLoad没有执行完毕,任务2要执行,就产生了死锁

b.异步主队列会不会产生死锁(不会)

- (void)viewDidLoad {[super viewDidLoad];NSLog(@"执行任务1");[self interview02];NSLog(@"执行任务3");
}- (void)interview02 {//以下代码是在主线程执行的,会不会产生死锁 会//队列:排队:FIFOdispatch_queue_t queue = dispatch_get_main_queue();dispatch_async(queue, ^{NSLog(@"执行任务2");});
}

dispatch_async不要求马上在当前线程同步执行任务

所以会先执行viewDidLoad里面的任务,执行完毕以后在执行async任务里面的方法

c.以下代码会不会产生死锁(会)

- (void)viewDidLoad {[super viewDidLoad];[self interview03];
}- (void)interview03 {NSLog(@"执行任务1");dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_SERIAL);dispatch_async(queue, ^{NSLog(@"执行任务2");dispatch_sync(queue, ^{NSLog(@"执行任务3");});NSLog(@"执行任务4");});NSLog(@"执行任务5");
}

 

串行队列,会先执行viewDidLoad里面的任务,然后在执行async里面的,虽然async会创建一个新的线程,但是是同步执行,所以还是先主线程执行完viewDidLoad里面的任务,在执行async里面的任务,但是block0执行完任务2,再执行任务3的时候的前提是执行完block0,但是sync又需要马上执行,就会产生死锁

d.以下代码会不会产生死锁(不会)

- (void)interview04 {NSLog(@"执行任务1");dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_SERIAL);dispatch_queue_t queue2 = dispatch_queue_create("myqueue2", DISPATCH_QUEUE_CONCURRENT);dispatch_async(queue, ^{NSLog(@"执行任务2");dispatch_sync(queue2, ^{NSLog(@"执行任务3");});NSLog(@"执行任务4");});NSLog(@"执行任务5");
}

这个会创建一个并发的队列,sync又要求立马执行,所以可以执行完任务2,在执行并发队列的任务3,在执行任务4

e. 以下代码会不会产生死锁 (不会)

- (void)interview05 {NSLog(@"执行任务1");dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_CONCURRENT);dispatch_async(queue, ^{NSLog(@"执行任务2");dispatch_sync(queue, ^{NSLog(@"执行任务3");});NSLog(@"执行任务4");});NSLog(@"执行任务5");
}

总结:哪种情况下会产生死锁?

使用sync函数当前串行队列中添加任务,会卡住当前的串行队列(产生死锁)

6)全局队列的地址是一个,从始至终用的都是一个队列,创建的并发队列,每创建一个都是不同的队列

    dispatch_queue_t queue1 = dispatch_get_global_queue(0, 0);dispatch_queue_t queue2 = dispatch_get_global_queue(0, 0);dispatch_queue_t queue3 = dispatch_queue_create("queue3", DISPATCH_QUEUE_CONCURRENT);dispatch_queue_t queue4 = dispatch_queue_create("queue4", DISPATCH_QUEUE_CONCURRENT);NSLog(@"%p,  %p  ,%p   %p",queue1,queue2,queue3,queue4);

 可以看出来全局的是一个地址,创建的是不同的地址

7)performSelector方法

a.

- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view.NSLog(@"1");//几秒之后再执行这个test方法[self performSelector:@selector(test) withObject:nil afterDelay:3.0];
}- (void)test {NSLog(@"2");
}

performSelector withObject afterDelay方法的作用是在几秒后执行@selector方法

一个是在47秒的时候,一个是在50秒的时候

b.

- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view.NSLog(@"1");//几秒之后再执行这个test方法[self performSelector:@selector(test) withObject:nil afterDelay:0.0];NSLog(@"3");
}- (void)test {NSLog(@"2");
}

performSelector withObject afterDelay这个方法是在Runloop里面添加了计时器,即使延迟0.0秒进行执行,也是会先执行下面的函数,函数在执行selector函数

c.

- (void)test {NSLog(@"2");
}- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {dispatch_queue_t queue = dispatch_get_global_queue(0, 0);dispatch_async(queue, ^{NSLog(@"1");
//        [self performSelector:@selector(test) withObject:nil afterDelay:0.0];[self performSelector:@selector(test) withObject:nil];NSLog(@"3");});
}

performSelector withObject函数的本质就是使用msgSend函数,所以正常执行

d.

- (void)test {NSLog(@"2");
}- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {dispatch_queue_t queue = dispatch_get_global_queue(0, 0);dispatch_async(queue, ^{NSLog(@"1");[self performSelector:@selector(test) withObject:nil afterDelay:0.0];NSLog(@"3");});
}

这个方法中test2就不执行了

原因就是 

performSelector withObject afterDelay的本质就是往RunLoop中添加定时器,子线程默认是不启动RunLoop的,所以没有RunLoop,这个代码不执行

如果想让这个代码执行的话,就是往子线程中添加RunLoop

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {dispatch_queue_t queue = dispatch_get_global_queue(0, 0);dispatch_async(queue, ^{NSLog(@"1");[self performSelector:@selector(test) withObject:nil afterDelay:0.0];NSLog(@"3");[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode];[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];});
}

这样的话test方法就可以执行了 

8)GNUstep

GNUstep是GNU计划的项目之一,它将Cocoa的OC库重新开源实现了一遍

源码地址:http://www.gnustep.org/resources/downloads.php

虽然GNUstep不是苹果的官方源码,但还是具有一定的参考价值

  dispatch_queue_t queue = dispatch_get_global_queue(0, 0);dispatch_sync(queue, ^{[NSTimer scheduledTimerWithTimeInterval:3.0 repeats:NO block:^(NSTimer * _Nonnull timer) {NSLog(@"123");}];});

在子线程当中不能执行NSTimer的操作,因为开启了一个子线程,就要执行block操作,执行完毕之后子线程就关闭了,3秒之后任务就不能执行了

9)面试题

 NSThread *thread = [[NSThread alloc]initWithBlock:^{NSLog(@"1");}];[thread start];[self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:YES];- (void)test {NSLog(@"2");
}

这个结果就是崩了,NSThread开启了一个子线程,然后子线程执行block里面的任务,主线程仍然往下执行,performSelector函数,执行test方法,但是在block执行完毕以后,子线程就结束了,所以执行不了test方法,就崩了,解决方案就是在子线程里面添加RunLoop,阻止子线程被杀掉

  NSThread *thread = [[NSThread alloc]initWithBlock:^{NSLog(@"1");[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode];[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];}];[thread start];[self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:YES];

4.GCD的队列类型

5.说一下OperationQueue和GCD的区别,以及各自的优势?

6.线程安全的处理手段有哪些?

7.OC你了解的锁有哪些?在你回答的基础上进行二次提问:

追问一:自旋和互斥对比?

追问二:使用以上锁需要注意哪些?

追问三:用C/C++/OC,任选其一,实现自旋与互斥,口述即可

七、苹果支付

1.ios支付主要分为两种,第三方支付和应用内支付(内购)

第三方支付:包括微信支付、支付宝支付、银联支付、百度钱包、京东支付等

应用内支付:在应用程序内购买虚拟商品,将收到支付金额的百分之七十

2.第三方支付

第三方支付的弹出方式有两种,网页和调用app,有些第三方支付没有安装客户端,可以直接弹出网页进行支付,手机中安装了客户端可以跳转到app进行支付,微信支付只能调用app进行支付

3.支付宝支付

1)支付宝开发平台:

https://open.alipay.com

2)移动支付集成

基本术语 | 网页&移动应用

3)商户服务平台(与支付宝签约需要填写的公司资料):

https://b.alipay.com/page/portal/home

4)支付流程

a.在商户服务平台先与支付宝签约,获得商户ID(partner)和账号ID(seller),需要提供公司资质或者营业执照,个人无法申请。

b.生成并下载相应的公钥私钥文件(加密签名用)

c.下载支付宝SDK:

d.生成订单信息

e.调用支付宝客户端,由支付宝客户端跟支付宝安全服务器打交道

f.支付完毕后返回支付结果给商户客户端和服务器

一、ios内存管理之野指针

1.野指针和空指针的概念

C语言中的野指针:声明一个指针变量,但是没有赋初始值,此时指针指向一个垃圾值,即指向一块随机的内存空间

OC中的野指针:指针所指向的对象已经被释放/回收了,但是指针没有作任何的修改,仍然指向已经回收的内存空间。

空指针:没有指向任何东西的指针,即nil、NULL、0,向空指针发送消息不会报错。

C中野指针事例: 此时没有对str2进行赋值,所以打印出来会是一个随机值,而且程序报错

OC中野指针事例:创建一个Person类的对象,创建完成之后将她release掉,此时的p就变成了野指针,需要注意的是虽然是野指针,但是不会报上述的错误,如果用对象调用其方法,也是可以执行。(是因为此时对象中的数据还可能在内存中)

2.僵尸对象(Zombie Object)

(1)僵尸对象:是一种用来检查内存错误(EXC_BAD_ACCESS)的对象,它可以捕获任何尝试访问坏内存的调用

(2)如果给僵尸对象发送消息,那么将在运行期间崩溃并输出错误日志,可以通过日志定位到野指针对象调用的方法和类名

(3)通俗来讲:僵尸对象就是指一个引用计数器为0、被释放的OC对象,此时这个对象的内存已经被系统回收,该对象可能还存在,即数据依然在内存中,但是此时僵尸对象已经不稳定了,其内存可能随时被别的对象申请占用,所以此时僵尸对象是不可以再访问和使用的。

3.内存回收的本质

(1)申请一块内存空间,其本质是向系统申请一块别人不再使用的内存空间,即已经回收的内存空间。

(2)释放一块内存空间,其本质是这个内存空间不再使用,可以由系统分配给别的对象使用,此时内存空间虽然回收了,但是原本的数据依赖是存在的,可以理解为垃圾数据,所以内存回收可以理解为以下两点

@1 OC对象释放后,内存回收,表示这一块内存可以分配给别的对象了;

@2 这块内存在分配给别的对象之前,仍然保留着已经释放对象的数据。

4.xcode能够检测僵尸对象,但是为什么不检查?

答:(1)会影响开发效率,(2)直接报错,来检查错误就可以

5.检测野指针,

在OC中野指针错误不是必现的,比如上面的例子,我们可以开始僵尸对象检测机制

比如有一行这样的代码

然后我们开启僵尸对象检测工具,选择Edit Scheme进入到下面的选项框,然后勾选检测僵尸对象按钮。

此时再运行项目

 

6. 将Person类变成了NSZombie类的底层实现分析。

(1)打开Instruments工具,选择zombie对象检测

(2)进入到检测页面,然后进行执行,将选择栏选到call tree下面,点击main函数进行分析,可以看到release方法其实是实现了父类的release方法

(3)可以看到底层调用了__dealloc_zombie方法,我们可以点击进去,直接复制这个方法

二、谈一谈你对ios内存管理的理解?

答:内存管理的主要作用是用来存储数据的,通过声明一个变量,可以将数据存储进去,

内存主要分为5大区域:栈、堆、BSS段、数据段和代码段,栈主要用来存储局部变量,当局部变量的作用域执行完毕之后,这个局部变量就会被回收掉,堆主要用来存储OC对象,BSS段主要用来存储未初始化的全局变量和静态变量,当全局变量和静态变量被初始化之后,就会被回收,并且存储到数据段之中,数据段主要用来存储初始化之后的全局变量和静态变量,当程序结束的时候被回收,代码段主要用来存储代码,当程序结束的时候被回收,存储在栈、BSS段、数据段和代码段中的数据系统会自动回收,所以内存管理我们只需要对存储在堆上的数据进行管理,内存管理的分类主要分为ARC和MRC,每一个对象都有一个retainCoun属性,用来记录这个对象有多少个人在使用。MRC是手动引用计数,遵循饿原则是谁创建谁销毁,当创建出来一个对象,他的引用计数器默认是1,(此时需要注意不是所有的对象创建出来以后引用计数器都是1,比如NSNumber,NSNumber一般创建的是自动释放的对象,自动释放的对象的retainCount也是不可靠的,NSString创建常量区对象时,他的retainCount默认也不是1)我们可以通过向这个对象发送retain消息来使他的引用计数器加1,通过发送release消息使他的引用计数器减1,直到他的引用计数器变为0,系统自动回收,并自动调用对象的delloc方法。ARC是自动引用计数,对象的计数管理完全由系统来管理,ARC的实现原理主要是在程序预编译阶段,将ARC的代码转换为非ARC的代码,自动加入retain、release、autorelease方法。内存管理还有自动释放池,自动释放池的原理就是将存入到自动释放池中的对象,在自动释放池被销毁的时候,会自动调用存在在自动释放池中的所有对象的release方法,我们可以通过autorelease方法将一个对象存储在自动释放池中,他仅仅是为对象发送一条release方法,并不是对对象进行了销毁。如果不能很好的对内存进行管理,就会造成内存泄露。

对NSNumber对象和NSString对象的默认引用计数验证:

二、内存问题

1.使用CADisplayLink,NSTimer有什么注意点?

答:CADisplayLink,NSTimer会对target产生强引用,如果target又对他们产生强引用,就会引发循环引用。

存在的问题:

1)我们创建一个CADisplayLink对象,希望在页面销毁的时候计时器销毁,页面也进行销毁

在viewDidLode中添加如下代码

//保证调用频率和屏幕的刷帧频率,60FPS,一秒钟调用60次self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(linkTest)];[self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

然后实现方法

- (void)linkTest {NSLog(@"%s",__func__);
}

页面销毁的方法,dealloc方法,注意页面销毁的delloc方法的调用,是当它back返回到上一个页面的时候才会调用

- (void)dealloc {[self.link invalidate];
}

此时运行项目会发现dealloc方法并没有执行,计时器也没有关闭,因为他们循环引用都没有销毁

2)NSTimer的使用也存在相应的问题

在viewDidLoad里面创建一个NSTimer的对象

- (void)viewDidLoad {[super viewDidLoad];
//    __weak typeof(self) weakSelf = self;
//    __weak ViewController *weakSelf1 = self;
//    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:weakSelf selector:@selector(timeTest) userInfo:nil repeats:YES];
}

执行方法

- (void)timeTest {NSLog(@"%s",__func__);
}

在页面销毁的时候,计时器销毁

- (void)dealloc {[self.timer invalidate];
}

此时执行同样发现,页面没有销毁,定时器也没有进行销毁。

思考:能否用__weak来修饰self避免循环引用?答案是不能,因为NSTimer本身对target进行了循环引用,我们改成weakSelf也只是传过去一个值,并不能修改NSTimer里面的强引用

解决方案:

解决方案一:使用block方式的初始化

__weak typeof(self) weakSelf = self;self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {[weakSelf timeTest];}]

解决方案二:创建一个中间量target,让他弱引用viewController。

 创建一个类继承自NSObject

LJProxy.h文件

#import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGIN@interface LJProxy : NSObject@property (nonatomic, weak) id target;+ (instancetype)proxyWithTarget:(id)target;@endNS_ASSUME_NONNULL_END

LJProxy.m文件:

#import "LJProxy.h"@implementation LJProxy+ (instancetype)proxyWithTarget:(id)target {LJProxy *proxy = [[LJProxy alloc]init];proxy.target = target;return proxy;
}//消息转发机制
- (id)forwardingTargetForSelector:(SEL)aSelector {return self.target;
}@end

在创建NSTimer对象的时候让target调用这个方法

 //解决方案二:self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[LJProxy proxyWithTarget:self] selector:@selector(timeTest) userInfo:nil repeats:YES];

此时就解决了这个问题

2.autorelease在什么时机会被释放?

四、分类和延展的区别

1.如何创建一个延展

1)右击文件夹选择New File

2)选择Objective-C File

3)选择extension

2.延展的介绍 

1)延展是1个特殊的分类,所以延展也是类的一部分

2)特殊之处:

a.延展这个特殊的分类没有名字

b.只有声明没有实现,和本类共享一个实现

3.延展的语法

@interface 本类名 ()

@end

没有实现,和本类共享一个实现

在本类中添加延展的头文件,在调用本类和延展的方法和属性的类中导入延展的头文件

#import <Foundation/Foundation.h>
#import "Person+itcast.h"int main(int argc, const char * argv[]) {@autoreleasepool {// insert code here...NSLog(@"Hello, World!");Person *person = [[Person alloc]init];[person run];[person sayHi];[person sleep];}return 0;
}

4.延展的基本使用

1)延展的本质是1个分类,作为本类的一部分,只不过是1个特殊的分类,没有名字

2) 延展只有声明,没有单独的实现,和本类共享一个实现

5.延展和分类的区别

1)分类有名字,而延展是一个匿名的分类,没有名字

2)每1个分类都有单独的声明和实现,而延展只有声明,没有单独的实现,和本类共享1个实现

3)分类只能新增方法,而延展当中任意的成员都可以添加,属性和方法

4)分类当中可以写@property,但是只会生成getter和setter的声明, 延展当中写@property会自动生成私有属性,也会生成getter和setter的声明和实现

6.延展的应用场景

​​​​​​​


http://chatgpt.dhexx.cn/article/nJY01aAR.shtml

相关文章

iOS面试题大全2021(附答案)

1、简述你项目中常用的设计模式。它们有什么优缺点&#xff1f; 常用的设计模式有&#xff1a;代理、观察者、单例。 &#xff08;1&#xff09;单例&#xff1a;它是用来限制一个类只能创建一个对象。这个对象中的属性可以存储全局共享的数据。所有的类都能访问、设置此单例…

iOS 中高级面试题(附答案)

RunLoop 1、什么是 RunLoop? RunLoop 作用有哪些&#xff1f; RunLoop 可以称之为运行循环&#xff0c;在程序运行过程中循环做一些事情&#xff0c;如果没有 RunLoop 程序执行完毕就会立即退出&#xff0c;有 RunLoop 程序会一直运行&#xff0c;并且时时刻刻在等待用户的输…

安装MyBatis教程

简单安装MyBatis教程 1. 介绍 MyBatis简介 1&#xff09; MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架 2&#xff09; MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集 3&#xff09; MyBatis可以使用简单的XML或注解用于配置和原…

mybatis简明教程

mybatis MyBatis 是一款优秀的持久层框架&#xff0c;它支持自定义 SQL、存储过程以及高级映射&#xff0c;本文将让您快速掌握mybatis开发 一&#xff1a; 简介 一只被烤黑了的鸟 MyBatis 是一款优秀的持久层框架&#xff0c;它支持自定义 SQL、存储过程以及高级映射。MyBa…

Mybatis教程-实战

1.从JDBC谈起 1.1.使用IDEA创建maven工程 1.2.引入mysql依赖包 <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.32</version> </dependency>1.3.准备数据 创建数据库&a…

SpringBoot使用Mybatis教程

文章目录 新建SpringBoot项目引入mybatis依赖如何使用mybatis&#xff1f;1.配置mybatis①.数据库配置②.mybatis相关配置 2.使用mybatis①.创建JavaBean②.创建mapper1).使用注解方式2&#xff09;.使用xml方式 ③.调用 新建SpringBoot项目 本文所使用的代码编辑器为IntelliJ…

MyBatis教程看这一篇就够啦,简单又全面(IDEA版)

目录 一、MyBatis简介 1.1 MyBatis介绍 为什么需要Mybatis&#xff1f; 二、MyBatis框架部署 2.1 创建Maven项目 2.2 在项目中添加MyBatis依赖 2.3 创建MyBatis配置文件 三、MyBatis框架使用 3.1 创建数据表 3.2 创建实体类 3.3 创建DAO接口&#xff0c;定义操作方法 …

MyBatis学习--完整教程

文章目录 MyBatis1、简介1.1 什么是Mybatis1.2 持久化1.3 持久层1.4 为什么需要MyBatis 2、第一个Mybatis程序2.1 搭建环境2.2 创建一个模块2.3 编写代码 3、CURD1. namespace2. select3. Insert4. update5. Delete6. 万能Map7. 模糊查询 4、配置解析1. 核心配置文件2. 环境配置…

mybatis详细教程

文章目录 [toc]1. 概述1.1 什么是Mybatis?1.2 Mybatis 操作数据库的方式1.3 Mybatis 操作数据库的七大步骤?1.4 Mybatis 的开发优点 2. Mybatis 操作数据库具体实现2.1 创建一个数据库表2.2 创建一个maven项目,配置pom.xml文件,导入相关依赖2.3 创建mybatis 核心配置文件2.4 …

MyBatis教程(看这一篇就够了)

一.全面了解Mybatis 环境变量 jdk 8 MySQL 8.0.27maven-3.6.1IDEA 2021.2.2 学习前需要掌握&#xff1a; JDBCMySQLJava基础MavenJunit&#xff08;单元测试&#xff09; 什么是MyBatis Myba是一款优秀的持久层框架MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及…

fsck命令使用

由于错误操作Linux导致系统无法正常开机&#xff0c;系统提示需要checking filesystems。如下图所示&#xff1a; 根据图中提示&#xff0c;先输入root用户密码进入root用户&#xff0c;然后在root用户中执行命令&#xff1a; fsck -f -y /dev/sda2 命令解释&#xff1a; fsc…

linux基本功之fsck命令详解

&#x1f493; 大家好&#xff0c;我是沐风晓月&#xff0c;双一流院校英语计算机双专业在读&#xff1b; &#x1f493; 想要学好Linux&#xff0c;命令是基本功&#xff0c;企业中常用的命令大约200多个&#xff0c;不管是写shell脚本还是管理操作系统&#xff0c;最常用的命…

fsck命令详解

fsck命令来自于英文词组“filesystem check”的缩写&#xff0c;其功能是用于检查与修复文件系统。若系统有过突然断电或磁盘异常的情况&#xff0c;建议使用fsck命令对文件系统进行检查与修复&#xff0c;以防数据丢失。 语法格式&#xff1a;fsck [参数] 文件系统 常用参…

hdfs fsck

转载来自&#xff1a;https://blog.csdn.net/zlfing/article/details/78070951 1.常用指令&#xff1a;打印文件块的位置信息 hdfs fsck /user/hadoop/wkz -files -blocks -locations生产实例&#xff1a;hdfs fsck *文件路径* -list-corruptfileblocks 线上环境降副本后&a…

Linux fsck 机制解析

Linux fsck 机制 0. 概述1. Upstart 方式 (ubuntu 14.04 32bit)1.1 配置fsck启动1.2 配置fsck运行参数1.3 mountall.config1.4 mountall1.5 fsck 2. Systemd 方式 (ubuntu 16.04 64bit)2.1 普通分区的fsck2.2 Root分区的fsck2.3 ext文件系统的限制2.4 强制fsck完整扫描2.5 fsck…

fsck异常

ubuntu 18 开机显示错误。&#xff08;完了&#xff0c;我的数据啊&#xff09; 莫慌&#xff01;&#xff01;&#xff01; 仔细看下报错。 原因大概是上次系统异常断电关机等非正常关机导致 磁盘损坏。 解决方法 fsck -y /dev/sdb6 sdb6为损坏的目标磁盘 修复完 重启就可以了…

Linux磁盘修复命令----fsck

linux下文件信息出现乱码&#xff0c;无法启动服务&#xff0c;也无法删除&#xff0c;改权限等等&#xff0c;那证明你的磁盘已损坏&#xff0c;需要修复&#xff0c;期间不会丢失数据&#xff0c;请放心操作 使用fsck命令修复磁盘时 一定要进入单用户模式去修复 语 法 fsck.…

fsck-磁盘修复工具

1、简介 fsck&#xff08;file system check&#xff09;用来检查和维护不一致的文件系统。若系统掉电或磁盘发生问题&#xff0c;可利用fsck命令对文件系统进行检查。 2、参数介绍 -a&#xff1a;自动修复文件系统&#xff0c;不询问任何问题&#xff1b;-A&#xff1a;依照/e…

linux命令中fsck命令 – 检查并修复Linux文件系统

fsck命令的英文全称是“filesystem check”&#xff0c;即检查文件系统的意思&#xff0c;常用于检查并修复Linux文件系统的一些错误信息&#xff0c;操作文件系统需要先备份重要数据&#xff0c;以防丢失。 Linux fsck命令用于检查并修复Linux文件系统&#xff0c;可以同时检…

压缩感知超分辨技术

1.技术原理 利用被测物体在时间、空间以及深度&#xff08;或在相应的变换域&#xff09;内的稀疏性&#xff0c;对信号进行少量的编码测量&#xff0c;而后利用重建算法还原原始信号。 实质&#xff1a;有限系统带宽下提升信息量 1.空间编码—超空间分辨率 优势&#xff1a…