收藏ios

2020 iOS 面试题集 2

2020-05-25  本文已影响0人  大湿的学徒

iOS程序内存分为几个区

iOS内存分为5大区域

  1. 栈区:编译器自动分配并释放,存放函数的参数值,局部变量等。栈是系统数据结构,对应线程/进程是唯一的。
  2. 堆区:由程序员分配和释放,如果程序员不释放,程序结束时,可能会由操作系统回收 ,比如在ios 中 alloc 都是存放在堆中。
  3. 全局区:全局变量和静态变量的存储是放在一起的,初始化的全局变量和静态变量存放在一块区域,未初始化的全局变量和静态变量在相邻的另一块区域,程序结束后由系统释放。
  4. 文字常量区:存放常量字符串,程序结束后由系统释放程序结束释放。
  5. 代码区:存放函数的二进制代码

iOS程序内存的每个分区怎么存储(举例说明)

代码区存放于低地址,栈区存放于高地址。区与区之间并不是连续的。堆区的内存是应用程序共享的,堆中的内存分配是系统负责的;当引用计数为0的时候,系统会回收该内存。

block一般存在哪里(分ARCMRC

代码区存储的是什么?

代码区存放的是程序中函数编译后的CPU指令

进程和线程的理解(从资源分配进行理解)

进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性

  1. 一个程序至少有一个进程,一个进程至少有一个线程。
  2. 线程的划分尺度小于进程,使得多线程程序的并发性高。

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。

进程线程的内存分配和管理

当程序被运行时,需要将可执行文件加载到内存,在内存中的可执行文件形成进程,一个进程(文件)可以同时存在多个进程(内存)
运行程序的时候,需要将可执行文件加载到内存中,形成进程。每个进程占据了一块独立的内存区域,这块内存区域又划分成不同的区域,从低地址到高地址依次为:代码区、只读常量区、全局区/数据区、BSS段、堆区、栈区 。

多线程中哪些内存是共享哪些独占

多线程中,线程之间有共享资源和独占资源。
共享资源有:

  1. 进程申请的堆内存
  2. 进程打开的文件描述符
  3. 进程的全局数据
  4. 进程id,进程组id
  5. 进程目录
  6. 信号处理器

独占资源有:

  1. 线程ID
  2. 寄存器组的值:每个线程有自己不同的运行线索,当从一个线 程切换到另一个线程上 时,必须将原有的线程的寄存器集合的状态保存,以便将来该线程在被重新切换到时能得以恢复。
  3. 线程堆栈
  4. 错误返回码
  5. 信号屏蔽码
  6. 线程的优先级

实现多线程同步的方式

同步方式有互斥锁(mutex),条件变量(condition variable)和读写锁(reader-writer lock)来同步资源
iOS 互斥锁有@synchronized
条件信号量dispatch_semaphore_t
NSConditionLock
读写锁 pthread_rwlock

两个异步子线程输出字符串,主线程前后也输出一个字符串,顺序如何,为什么是这样的?

先执行主线程种操作,在执行一步子线程操作,子线程在分配时遵循,新建,就续,运行,阻塞,死亡这个生命周期,而主线程已经运行状态,所以会先运行主线程的操作,操作遵循FIFO的模式进行。子线程如果没有特殊的优先级指定,默认处于同一优先级,所以也遵循FIFO的模式运行。

任务A,B,C先执行A和B再执行C可以怎么实现(group,条件锁,barrier)

  1. group 通过创建信号量访问资源数量为1,然后通过wait和sign顺序执行group内线程。

  2. NSConditionLock,通过控制创建条件和解锁条件,顺序执行线程。

let lock = NSConditionLock.init(condition: 3)
        DispatchQueue.global().async {
            lock.lock(whenCondition: 3)
            print("A")
            lock.unlock(withCondition: 2)
        }
        DispatchQueue.global().async {
                   lock.lock(whenCondition: 2)
                   print("B")
                   lock.unlock(withCondition: 1)
               }
        DispatchQueue.global().async {
                   lock.lock(whenCondition: 1)
                   print("C")
                   lock.unlock()
               }
  1. barrier 允许在一个并发队列中创建一个同步点。当在并发队列中遇到一个barrier, 他会延迟执行barrier的block,等待所有在barrier之前提交的blocks执行结束。 这时,barrier block自己开始执行。

属性的修饰关键词有哪些

见上一篇 面试题集

atomicnonatomic的区别,如果是你觉得该怎么实现atomic一样的效果

主要区别在于atomic 保证 get、set 操作的完整性。
可以堆get,set操作加锁,实现atomic一样的功能。

atomic 一定是线程安全的吗?什么情况下是不安全的?

不一定是线程安全的,只保证了get,set操作安全,但是不保证资源线程的安全。

如果一个线程正在getter 或者 setter时,有另外一
个线程同时对该属性进行release操作,如果release
先完成,会造成crash

copy常用来修饰什么,为什么?

常常用来修饰NSString,使用copy修饰之后,即使属性拷贝来自可变字符串,也会被深拷贝成不可变字符串,也就是源字符串修改之后不会影响到属性字符串,增强了代码的健壮性。

weakassign 的区别

见上一篇 面试题集

delegate你一般用什么修饰(回答weak,为什么?可以用`assign吗)

见上一篇 面试题集

循环引用(weak,用assign修饰block可以吗)

见上一篇 面试题集

KVO的实现原理(runtime)或者你要实现KVO你会怎么做

KVO运用了一个isa-swizzling技术,isa-swizzling就是混合指针机制,将2个对象的isa指针互相调换。
当某个类的属性对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的 setter 方法。派生类在被重写的setter方法内实现真正的通知机制。
每个类对象中都有一个isa指针指向当前类,当一个类对象的第一次被观察,那么系统会偷偷将isa指针指向动态生成的派生类,从而在给被监控属性赋值时执行的是派生类的setter方法
键值观察通知依赖于NSObject的两个方法: willChangeValueForKey:didChangevlueForKey:
在一个被观察属性发生改变之前, willChangeValueForKey:一定会被调用,这就 会记录旧的值。而当改变发生后,didChangeValueForKey:会被调用,继而 observeValueForKey:ofObject:change:context:也会被调用。

旋转数组找最小数(算法)

思路:

  1. 从头到尾遍历数组一次,就能找出最小的元素,时间复杂度显然是O(n)。
  2. 通过二分查找的方式,时间复杂度为O(logn)观察一下数组的特性,首先递增(称为递增a),然后突然下降到最小值,然后再递增(称为递增b)。当然还有一种特殊情况,就是数组递增,中间没有下降,即旋转元素个数为0。对于一般的情况,假设A为输入数组,left 和 right 为数组左右边界的坐标,考察中间位置的值A[mid] ,如果A[mid] <= A[right],表明处于递增b,调整右边界 right = mid;如果A[mid] >= A[left],表明处于递增a,因此调整左边界left = mid。当左右边界相邻时,较小的一个就是数组的最小值。其实,对于一般情况,右边界所指的元素为最小值。对于特殊情况,即旋转个数为0。按照上述算法,右边界会不断减少,直到与左边界相邻。这时左边界所指的元素为最小值。
//# Swift 实现

func findMin( pArray:[Int]) -> Int{
        let len = pArray.count
        if len <= 0 { return 0 };
        var left:Int = 0
        var right:Int = len - 1
        var mid:Int = 0
        while(right - left != 1)
        {
            mid = left + ((right - left)>>1);
            if pArray[right] >= pArray[mid] {
                right = mid;
            } else if pArray[left] <= pArray[mid] {
                     left = mid
            }
        }
        return pArray[right] > pArray[left] ? pArray[left] : pArray[right]
    }
上一篇下一篇

猜你喜欢

热点阅读