iOS 面试题简单总结(不定期更新)

目录

 

如果有错误,欢迎指出,谢谢

1.内存的存储

2.内存管理

3.多线程

4.GCD 简单使用

 · GCD线程之间的通信 

· GCD数据安全(加解锁) 

5.NSOperation,NSOperationQueue

· 使用步骤

· 串并行设置(并发数控制)

· 操作依赖

· 优先级(queuePriority)

·线程间的通信

·线程安全

·NSOperation 其他常用函数

·NSOperationQueue 其他常用函数

6.GCD与NSOperation

7.线程管理

8.线程安全

9.block和Delegate

10.Hybrid


如果有错误,欢迎指出,谢谢

1.内存的存储

在iOS中,内存存在于

栈区(运行时分配)  存放:局部变量和方法实参     不需要程序员管理   栈的增长方向是向下的,即向着内存地址减小的方向。

堆区(运行时分配)  存放:OC通过new,alloc,copy, mutableCopy创建的对象      通过引用计数进行管理(NSObject属性retainCount), 但是Core Function对象不在此列,需要程序员自己管理(CFRetain,CFRelease)    堆的增长方向是向上的,即向着内存地址增加的方向

全局区(静态区,编译时分配??(是这样吗?求解答)),存放:全局变量和静态变量(static floar num = 10.0;)    分为BSS段(未初始化数据), 分为Data段(已初始化阶段)

常量区(数据段,编译时分配) 存放:常量字符串

代码区(编译时分配,二进制形式)  存放:程序中的代码

2.内存管理

程序员主要管理的内存是堆区内存,主要通过引用计数,对象的初始化,以及强引用,都会导致引用计数+1, 当持有对象被释放,引用计数会减1,或者通过- release方法(MRC)。 当引用计数为0的时候,该对象释放(如果指针仍指向这个对象的内存,野指针)

内存管理主要是考虑内存泄漏的问题,内存泄漏会导致,app运行内存越来越大,根本原因是一些本应该被释放的对象没有得到释放无法回收内存。

造成内存泄漏的常见问题有,互相强引用,block(使用copy声明,block中使用weakself),delegate(weak声明),NSTimer(外部调用-invalidate(不能再dealloc中),并置nil), 非OC对象(比如CF*** 需要手动管理) , 地图(及时置nil), 产生大量临时变量(例如在for循环中,使用 @autoreleasepool)

3.多线程

多线程 GCD以及NSOperation 

NSoperation是对GCD的封装。

GCD在不复杂的场景式使用,代码量更为轻便一点。如果使用场景复杂的时候,推荐使用NSoperation

4.GCD 简单使用

GCD的核心概念:任务(block中执行的,任务是同步任务还是异步任务,是决定了是否开启子线程的主要原因(main_queue例外)),队列(用来管理任务的一个队,分为串行和并行两种,采用FIFO先进先出的原则, 队列主要作用是用来管理任务的,而根据队列性质的不同,来决定是否开辟子线程), 串行队列是在上一个任务执行完以后,才去执行下一个。并行队列是在上一个任务开始执行后,再去执行下一个

    dispatch_queue_t  serial_queue = dispatch_queue_create("xxx", DISPATCH_QUEUE_SERIAL);//串行队列
    dispatch_queue_t  concurrent_queue = dispatch_queue_create("xxx", DISPATCH_QUEUE_CONCURRENT);//并行队列
    dispatch_sync(serial_queue,^{});//同步串行
    dispatch_sync(concurrent_queue,^{});//同步并行
    dispatch_async(serial_queue,^{});//异步串行
    dispatch_async(concurrent_queue,^{});//异步并行

 系统有为我们提供了两个常用队列Global Queue(并行队列) 和 Main Queue(串行队列)。 经常混合使用,使用场景(子线程获取一张图片,再回归主线程刷新UI)

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSLog(@"异步线程");
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"异步主线程");
    });
});

需要注意的是:如果使用的是主队列(main queue),同步执行会导致主线程死锁(追加的任务和主线程本身的任务冲突,互相等待,死锁卡住), 而主队列异步执行并不会开启新的线程(先执行已添加的任务,再执行队列中的)

 

 

 · GCD线程之间的通信 

在线程GCD_A的block中执行GCD_B即可

· GCD数据安全(加解锁) 

semaphore(信号量),用来控制线程并发的最大访问量,为1的时候,表示同时只允许一条线程访问,保证线程同步

semaphoreLock = dispatch_semaphore_create(1); //创建锁 在声明队列的区域里声明


// 相当于加锁  在任务执行前调用
dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);

// 相当于解锁  在任务执行后调用
dispatch_semaphore_signal(semaphoreLock);


· GCD任务执行顺序总结

在同一个方法A中,如果出现了同步执行,则会按照代码顺序执行,不管是否是bloc中的NSLog,或者是直接NSLog,(所以这种情况,同步串行主队列会死锁。。因为block要执行,A也正在执行(都在主线程中), 又因为是串行,一次只能执行一个,谁也执行不了,gg)。 而异步执行,则会先执行直接NSLog的,再按照FIFO执行block中的NSLog

5.NSOperation,NSOperationQueue

参考文章:iOS 多线程:『NSOperation、NSOperationQueue』详尽总结

是基于GCD的更高一级的封装,完全面向对象,可操作性更高。

NSOperation 本身是一个抽象类,使用前,必须子类化,系统提供了两个子类NSInvocationOperation 和 NSBlockOperation。两者最大的区别,前者使用SEL, 后者使用block, 并且NSBlockOperation 提供了- addExecutionBlock,这时添加的操作,是和blockOperationWithBlock:的操作并行执行的, 如果一个NSBlockOperation添加了一定量的其他操作,则blockOperationWithBlock:的会导致的操作不一定在在主线程中执行(如果使用的主队列)

NSOperationQueue 操作队列,类似GCD中的dispatch_queue,单不同于dispatch_queue的FIFO原则,NSOperationQueue是将所有任务准备就绪后,根据优先级来处理。 通过maxConrurrentOperationCount来控制线程数量。NSOperationQueue提供了两种不同的队列,主队列和自定义队列,主队列

 NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    // 2.设置最大并发操作数
    queue.maxConcurrentOperationCount = 1; // 串行队列
// queue.maxConcurrentOperationCount = 2; // 并发队列
// queue.maxConcurrentOperationCount = 8; // 并发队列

· 使用步骤

1.创建NSOperation

2.创建NSOperationQueue

3.将NSOperation加入到NSOperationQueue中

// 1.创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    // 2.创建操作
    // 使用 NSInvocationOperation 创建操作1
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];

    // 使用 NSInvocationOperation 创建操作2
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task2) object:nil];

    // 使用 NSBlockOperation 创建操作3
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"3---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];
    [op3 addExecutionBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"4---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];

    // 3.使用 addOperation: 添加所有操作到队列中
    [queue addOperation:op1]; // [op1 start]
    [queue addOperation:op2]; // [op2 start]
    [queue addOperation:op3]; // [op3 start]

· 串并行设置(并发数控制)

设置 NSOperationQueue的maxConcurrentOperationCount。  1时为串行  -1为不控制   大于1时为并行

· 操作依赖

当我们需要执行完B之后,再去执行A,这时候就需要用到了NSOperation的依赖Dependency。 需要注意的是,即使op1依赖于op2,op2和op1都要加入queue中,不能只将op1 加入到队列中,如果只将op1加入队列中,op1将永远不会执行,因为一直在等待op2执行完毕, 而op2并没有加入queue中,所以不会执行。

此外还会出现,op1 依赖op2  但是op2依赖op1, 互相依赖。。。两个都不会执行,且不会有任何异常。。俩人干瞪眼。。

关于依赖,常用的3个API

- (void)addDependency:(NSOperation *)op; //添加依赖,使当前操作依赖于操作 op 的完成。

- (void)removeDependency:(NSOperation *)op;// 移除依赖,取消当前操作对操作 op 的依赖。

@property (readonly, copy) NSArray<NSOperation *> *dependencies; //在当前操作开始执行之前完成执行的所有操作对象数组。

· 优先级(queuePriority)

优先级的操作,适用于同一队列的不同操作,对于不同队列的,是无效的。

// 优先级的取值
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
};

优先级是对执行顺序一种控制,但是并不能替代依赖关系,上面提到的NSOperationQueue是根据准备就绪的操作的优先级,来决定顺序的,而针对同一优先级的准备就绪的不同操作,执行顺序,则按照加入queue的先后顺序执行。

准备就绪:对于准备就绪的解释,op1(VeryLow) 依赖与op2(High), 那么执行顺序是 op1, op2。因为此时op2是非准备就绪的,而op1是已经准备就绪的,当op1执行完成后,op2才准备就绪。

一个队列中,①优先级相同,而且全部都是准备就绪的操作,则按照加入queue的顺序执行。②优先级不同,全部准备就绪,则按照优先级进行,③如果有准备就绪的和未准备就绪的,则会优先执行准备就绪的,而不是看优先级,所有优先级不能替代依赖。④如果代码上,第一个加入队列的是准备就绪的队列,则会忽略优先级,直接第一个执行

·线程间的通信

通常情况下,是在子线程获取数据后,在主线程刷新。 直接为主队列添加操作即可

·线程安全

如果多个线程,读写同一个变量,可能会导致所获得值和预期值有出入的情况。此时的线程是不安全的,需要使用NSLock(也可以使用 @synchronized)。

加锁前后

·NSOperation 其他常用函数

  • 取消操作方法
    • - (void)cancel;可取消操作,实质是标记 isCancelled 状态。
  • 判断操作状态方法
    • - (BOOL)isFinished;判断操作是否已经结束。
    • - (BOOL)isCancelled;判断操作是否已经标记为取消。
    • - (BOOL)isExecuting;判断操作是否正在在运行。
    • - (BOOL)isReady;判断操作是否处于准备就绪状态,这个值和操作的依赖关系相关。
  • 操作同步
    • - (void)waitUntilFinished;阻塞当前线程,直到该操作结束。可用于线程执行顺序的同步。
    • - (void)setCompletionBlock:(void (^)(void))block;completionBlock会在当前操作执行完毕时执行 completionBlock。
    • - (void)addDependency:(NSOperation *)op;添加依赖,使当前操作依赖于操作 op 的完成。
    • - (void)removeDependency:(NSOperation *)op;移除依赖,取消当前操作对操作 op 的依赖。
    • @property (readonly, copy) NSArray<NSOperation *> *dependencies;在当前操作开始执行之前完成执行的所有操作对象数组。

·NSOperationQueue 其他常用函数

  • 取消/暂停/恢复操作
    • - (void)cancelAllOperations;可以取消队列的所有操作。
    • - (BOOL)isSuspended;判断队列是否处于暂停状态。 YES 为暂停状态,NO 为恢复状态。
    • - (void)setSuspended:(BOOL)b;可设置操作的暂停和恢复,YES 代表暂停队列,NO 代表恢复队列。
  • 操作同步
    • - (void)waitUntilAllOperationsAreFinished;阻塞当前线程,直到队列中的操作全部执行完毕。
  • 添加/获取操作`
    • - (void)addOperationWithBlock:(void (^)(void))block;向队列中添加一个 NSBlockOperation 类型操作对象。
    • - (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait;向队列中添加操作数组,wait 标志是否阻塞当前线程直到所有操作结束
    • - (NSArray *)operations;当前在队列中的操作数组(某个操作执行结束后会自动从这个数组清除)。
    • - (NSUInteger)operationCount;当前队列中的操作数。
  • 获取队列
    • + (id)currentQueue;获取当前队列,如果当前线程不是在 NSOperationQueue 上运行则返回 nil。
    • + (id)mainQueue;获取主队列。

上述函数中的取消和暂停操作,不是立即取消或者暂停,而是停止接下来的操作

· NSOperation状态监听

//监听NSOperation执行完毕
- (void (^)(void))completionBlock;
- (void)setCompletionBlock:(void (^)(void))block;

或者使用KVO, 监听上面提到的属性。

6.NSThread

参考文章:关于iOS多线程,我说,你听,没准你就懂了!

NSThread是apple提供的面向对象的线程管理,提供的API

@interface NSThread : NSObject
//当前线程
@property (class, readonly, strong) NSThread *currentThread;
//使用类方法创建线程执行任务
+ (void)detachNewThreadWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
//判断当前是否为多线程
+ (BOOL)isMultiThreaded;
//指定线程的线程参数,例如设置当前线程的断言处理器。
@property (readonly, retain) NSMutableDictionary *threadDictionary;
//当前线程暂停到某个时间
+ (void)sleepUntilDate:(NSDate *)date;
//当前线程暂停一段时间
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
//退出当前线程
+ (void)exit;
//当前线程优先级
+ (double)threadPriority;
//设置当前线程优先级
+ (BOOL)setThreadPriority:(double)p;
//指定线程对象优先级 0.0~1.0,默认值为0.5
@property double threadPriority NS_AVAILABLE(10_6, 4_0);
//服务质量
@property NSQualityOfService qualityOfService NS_AVAILABLE(10_10, 8_0);
//线程名称
@property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);
//栈区大小
@property NSUInteger stackSize NS_AVAILABLE(10_5, 2_0);
//是否为主线程
@property (class, readonly) BOOL isMainThread NS_AVAILABLE(10_5, 2_0);
//获取主线程
@property (class, readonly, strong) NSThread *mainThread NS_AVAILABLE(10_5, 2_0);
//初始化
- (instancetype)init NS_AVAILABLE(10_5, 2_0) NS_DESIGNATED_INITIALIZER;
//实例方法初始化,需要再调用start方法
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument NS_AVAILABLE(10_5, 2_0);
- (instancetype)initWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
//线程状态,正在执行
@property (readonly, getter=isExecuting) BOOL executing NS_AVAILABLE(10_5, 2_0);
//线程状态,正在完成
@property (readonly, getter=isFinished) BOOL finished NS_AVAILABLE(10_5, 2_0);
//线程状态,已经取消
@property (readonly, getter=isCancelled) BOOL cancelled NS_AVAILABLE(10_5, 2_0);
//取消,仅仅改变线程状态,并不能像exist一样真正的终止线程
- (void)cancel NS_AVAILABLE(10_5, 2_0);
//开始
- (void)start NS_AVAILABLE(10_5, 2_0);
//线程需要执行的代码,一般写子类的时候会用到
- (void)main NS_AVAILABLE(10_5, 2_0);
@end

另外,还有一个NSObject的分类,瞅一眼:
@interface NSObject (NSThreadPerformAdditions)
//隐式的创建并启动线程,并在指定的线程(主线程或子线程)上执行方法。
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array NS_AVAILABLE(10_5, 2_0);
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg NS_AVAILABLE(10_5, 2_0);
@end

 

7.GCD与NSOperation

共同点:都是对多线程的一种操作方式

不同点:GCD采用Block的方式,使用起来简单方便,但是自由度较低。启用几条子线程由系统控制。

               NSOperation则是完全面向对象,使用SEL,自由度更高,特色:依赖,自由控制线程数(系统有默认上限,一般为核数的2倍),设定优先级,方便的取消操作。启用几条子线程由程序员控制maxConcurrentOperationCount(-1 == 不限制)。4

 

8.ViewController的生命周期

1. initWithCoder:         通过 nib 文件初始化时触发。
2. awakeFromNib:          nib 文件被加载的时候,会发生一个 awakeFromNib 的消息到 nib 文件中的每个对象。      
3. loadView:              开始加载视图控制器自带的 view。
4. viewDidLoad:           视图控制器的 view 被加载完成。  
5. viewWillAppear:        视图控制器的 view 将要显示在 window 上。
6. updateViewConstraints: 视图控制器的 view 开始更新 AutoLayout 约束。
7. viewWillLayoutSubviews:视图控制器的 view 将要更新内容视图的位置。
8. viewDidLayoutSubviews: 视图控制器的 view 已经更新视图的位置。
9. viewDidAppear:         视图控制器的 view 已经展示到 window 上。 
10. viewWillDisappear:    视图控制器的 view 将要从 window 上消失。
11. viewDidDisappear:     视图控制器的 view 已经从 window 上消失。
12. dealloc;               销毁

.线程安全

.block和Delegate

.Hybrid

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值