一、iOS程序内存分为几个区
iOS内存分为5大区域
1. 栈区:编译器自动分配并释放,存放函数的参数值,局部变量等。栈是系统数据结构,对应线程/进程是唯一的。
2. 堆区:由程序员分配和释放,如果程序员不释放,程序结束时,可能会由操作系统回收 ,比如在iOS
中 alloc
都是存放在堆中。
3. 全局区:全局变量和静态变量的存储是放在一起的,初始化的全局变量和静态变量存放在一块区域,未初始化的全局变量和静态变量在相邻的另一块区域,程序结束后由系统释放。
4. 文字常量区:存放常量字符串,程序结束后由系统释放程序结束释放。
5. 代码区:存放函数的二进制代码
二、iOS程序内存的每个分区怎么存储(举例说明)
- 栈区:存放的局部变量、先进后出、一旦出了作用域就会被销毁;函数跳转地址,现场保护等,内存地址从高到低分配。
-
堆区:堆区的地址是从低到高分配,通过程序员通过
alloc
手动分配。 - 全局区:包含两个部分,未初始化区,初始化区域。全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域;
代码区存放于低地址,栈区存放于高地址。区与区之间并不是连续的。堆区的内存是应用程序共享的,堆中的内存分配是系统负责的;当引用计数为0的时候,系统会回收该内存。
三、block一般存在哪里(分ARC和MRC)
- 在
MRC
下,Block
默认是分配在栈上的,除非进行显式执行的copy
方法,只要block没有引用外部的局部变量,block
放在全局区里面 - 在
ARC
的中,对象默认是用__strong
修饰的,所以大部分情况下编译器都会将block
从栈自动复制到堆上。有一个特殊情况,如果仅仅定义了block
没有赋值给变量的话,仍是在栈上。这种情况下随着作用域结束,block
将会销毁回收。
四、代码区存储的是什么?
代码区存放的是程序中函数编译后的CPU
指令
五、进程和线程的理解(从资源分配进行理解)
进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性
1. 一个程序至少有一个进程,一个进程至少有一个线程。
2. 线程的划分尺度小于进程,使得多线程程序的并发性高。
线程是进程的一个实体,是CPU
调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
六、进程线程的内存分配和管理
当程序被运行时,需要将可执行文件加载到内存,在内存中的可执行文件形成进程,一个进程(文件)可以同时存在多个进程(内存)
运行程序的时候,需要将可执行文件加载到内存中,形成进程。每个进程占据了一块独立的内存区域,这块内存区域又划分成不同的区域,从低地址到高地址依次为:代码区、只读常量区、全局区/数据区、BSS段、堆区、栈区 。
7、多线程中哪些内存是共享哪些独占
多线程中,线程之间有共享资源和独占资源。
共享资源有:
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()
}
3. barrier
允许在一个并发队列中创建一个同步点。当在并发队列中遇到一个barrier
, 他会延迟执行barrier
的block
,等待所有在barrier
之前提交的blocks
执行结束。 这时,barrier block
自己开始执行。
十一、属性的修饰关键词有哪些
十二、atomic
和nonatomic
的区别,如果是你觉得该怎么实现atomic
一样的效果
主要区别在于atomic
保证 get
、set
操作的完整性。
可以堆get
,set
操作加锁,实现atomic
一样的功能。
十三、atomic
一定是线程安全的吗?什么情况下是不安全的?
不一定是线程安全的,只保证了get
,set
操作安全,但是不保证资源线程的安全。
如果一个线程正在getter
或者 setter
时,有另外一个线程同时对该属性进行release
操作,如果release
先完成,会造成crash
十四、copy
常用来修饰什么,为什么?
常常用来修饰NSString
,使用copy
修饰之后,即使属性拷贝来自可变字符串,也会被深拷贝成不可变字符串,也就是源字符串修改之后不会影响到属性字符串,增强了代码的健壮性。
十五、weak
和assign
的区别
十六、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:
也会被调用。
十九、旋转数组找最小数(算法)
思路:
- 从头到尾遍历数组一次,就能找出最小的元素,时间复杂度显然是
O(n)
。 - 通过二分查找的方式,时间复杂度为
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]
}
推荐文集
收录:原文地址
网友评论