我的iOS面试题

作者: _ToBeBetterMan | 来源:发表于2016-05-13 19:57 被阅读1028次

    从今天开始每天总结几道iOS面试题!!!

    Day--1

    1.#include与#import的区别、#import与@class的区别

    a.#include与#import的区别:#include用于对系统文件的引用,编译器会在系统文件目录下去查找该文件。#include与#import 都会包含引用的类的所有信息,包括实体变量和方法,但是#import处理了重复引用的问题,而include需要自己处理重复引用。

    b.#import与@class的区别:1.import会包含这个类的所有信息,包括实体变量和方法,而@class只是告诉编译器,其后面声明的名称是类的名称,至于这些类是如何定义的,暂时不用考虑,后面会再告诉你。2.在头文件中, 一般只需要知道被引用的类的名称就可以了。 不需要知道其内部的实体变量和方法,所以在头文件中一般使用@class来声明这个名称是类的名称。 而在实现类里面,因为会用到这个引用类的内部的实体变量和方法,所以需要使用#import来包含这个被引用类的头文件。

    2.深、浅拷贝的基本概念以及他们的区别

    深拷贝 : 拷贝出来的对象与源对象地址不一致! 这意味着我修改拷贝对象的值对源对象的值没有任何影响.

    浅拷贝 : 拷贝出来的对象与源对象地址一致! 这意味着我修改拷贝对象的值会直接影响到源对象.

    这里有更详细的介绍,以及copy的详细的用法详解。让我们谢谢原作者,我只是搬运工

    3. 什么情况使用 weak 关键字,相比 assign 有什么不同?

    一.什么情况使用 weak 关键字

    1)在ARC中,在有可能出现循环引用的时候,往往要通过让其中一端使用weak来解决,比如:delegate代理属性

    2)自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用weak,自定义IBOutlet控件属性一般也使用weak;当然,也可以使用strong。

    二.不同点:

    1)weak 此特质表明该属性定义了一种“非拥有关系” (nonowning relationship)。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign类似, 然而在属性所指的对象遭到摧毁时,属性值也会清空(nil out)。 而 assign 的“设置方法”只会执行针对“纯量类型” (scalar type,例如 CGFloat 或 NSlnteger 等)的简单赋值操作。

    2)使用weak不会让引用计数器+1,weak当对象销毁的时候,指针会被自动设置为nil,而assign不会* assigin 可以用非OC对象,而weak必须用于OC对象。

    4. @property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的

    @property 的本质 :@property = ivar + getter + setter;

    “属性” (property)有两大概念:ivar(实例变量)、存取方法(access method = getter + setter)。

    ivar、getter、setter 是如何生成并添加到这个类中的?

    完成属性定义后,编译器会自动编写访问这些属性所需的方法,此过程叫做“自动合成”( autosynthesis)。需要强调的是,这个过程由编译 器在编译期执行,所以编辑器里看不到这些“合成方法”(synthesized method)的源代码。除了生成方法代码 getter、setter 之外,编译器还要自动向类中添加适当类型的实例变量,并且在属性名前面加下划线,以此作为实例变量的名字。

    也就是说我们每次在增加一个属性,系统都会在ivar_list中添加一个成员变量的描述,在method_list中增加setter与getter方法的描述,在属性列表中增加一个属性的描述,然后计算该属性在对象中的偏移量,然后给出setter与getter方法对应的实现,在setter方法中从偏移量的位置开始赋值,在getter方法中从偏移量开始取值,为了能够读取正确字节数,系统对象偏移量的指针类型进行了类型强转.

    Day--2

    5. @synthesize和@dynamic有什么区别

    • @property有两个对应的词,一个是 @synthesize,一个是 @dynamic。如果 @synthesize和 @dynamic都没写,那么默认的就是@syntheszie var = _var;
    • @synthesize 的语义是如果你没有手动实现 setter 方法和 getter 方法,那么编译器会自动为你加上这两个方法。
    • @dynamic 告诉编译器:属性的 setter 与 getter 方法由用户自己实现,不自动生成。(当然对于 readonly 的属性只需提供 getter 即可)。假如一个属性被声明为 @dynamic var,然后你没有提供 @setter方法和 @getter 方法,编译的时候没问题,但是当程序运行到 instance.var = someVar,由于缺 setter 方法会导致程序崩溃;或者当运行到 someVar = var 时,由于缺 getter 方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。

    6. GCD底层的三种队列和简单理解

    1.the main queue: 与主线程功能相同。是一个串行队列。
    主队列为串行队列,可以又叫 UI队列,关于处理UI界面的请求都在主队列中异步并发执行。
    2.global queues: 全局队列是并发队列,并由整个进程共享。global_queue是一个全局并发队列,即加入global_queue的代码块会立即被执行
    3.用户队列: 用户队列是串行的

    7. 请简述AFNetWorking的实现原理

    • NSURLConnection + NSOperation

    • NSURLConnection 是 Foundation URL 加载系统的基石。一个 NSURLConnection 异步地加载一个NSURLRequest 对象,调用 delegate 的 NSURLResponse / NSHTTPURLResponse 方法,其 NSData 被发送到服务器或从服务器读取;delegate 还可用来处理 NSURLAuthenticationChallenge、重定向响应、或是决定 NSCachedURLResponse 如何存储在共享的 NSURLCache 上。

    • NSOperation 是抽象类,模拟单个计算单元,有状态、优先级、依赖等功能,可以取消。

    • AFNetworking 的第一个重大突破就是将两者结合。AFURLConnectionOperation 作为 NSOperation 的子类,遵循 NSURLConnectionDelegate 的方法,可以从头到尾监视请求的状态,并储存请求、响应、响应数据等中间状态。

    8. CALayer和UIView的关系

    1.UIView本身不具备显示的功能,是它内部的CALayer在起作用

    2.CALayer是图层,和界面展示相关。UIView很多属性和方法和CALayer里的属性和方法是一致的。UIView可以看做是CALayer的管理者,除了负责视图展示之外,还可以处理一些事件,比如手势交互等。但我们对UIView做的一些有关视图显示和动画的操作,本质上还是对CALayer进行的操作。

    3.CALayer 和UIView的选择

    •通过CALayer,就能做出跟UIImageView一样的界面效果

    •既然CALayer和UIView都能实现相同的显示效果,那究竟该选择谁好呢?

    其实,对比CALayer,UIView多了一个事件处理的功能。也就是说,CALayer不能处理用户的触摸事件,而UIView可以;所以,

    如果显示出来的东西需要跟用户进行交互的话,用UIView;

    如果不需要跟用户进行交互,用UIView或者CALayer都可以

    Day--3

    9. iOS中有哪些数据持久化方式,请简要介绍它们的应用场景

    iOS下数据持久化常用的几种方式:

    • NSUserDefaults
    • plist(属性列表)
    • NSKeyedArchiver(对象归档)
    • iOS的嵌入式关系数据库SQLite3
    • 苹果公司提供的持久化工具 Core Data

    上面几种方式,有一个共同的要素,就是应用的/Documents文件夹。每个应用都有自己的/Documents文件夹,且仅能读写各自/Documents文件中的内容。

    10. 事件是如何传递的,简述事件响应链机制

    • 响应者链条:是由多个响应者对象连接起来的链条
    • 作用:清楚的看见每个响应者之间的联系,并且可以让一个事件由多个对象处理。
    • 响应者对象:有响应和处理事件能力的对象。

    事件的产生和传递过程:

    1.发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的队列事件中

    2.UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常会先发送事件给应用程序的主窗口(keyWindow)

    3.主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件

    4.找到合适的视图控件后,就会调用视图控件的touches方法来作事件的具体处理:touchesBegin… touchesMoved…touchesEnded等

    5.这些touches方法默认的做法是将事件顺着响应者链条向上传递,将事件叫个上一个相应者进行处理

    响应者链条其实就是很多响应者对象(继承自UIResponder的对象)一起组合起来的链条称之为响应者链条

    一般默认做法是控件将事件顺着响应者链条向上传递,将事件交给上一个响应者进行处理。那么如何判断当前响应者的上一个响应者是谁呢?有以下两个规则:

    1.判断当前是否是控制器的View,如果是控制器的View,上一个响应者就是控制器

    2.如果不是控制器的View,上一个响应者就是父控件

    UIApplication–>UIWindow–>递归找到最合适处理的控件–>控件调用touches方法–>判断是 否实现touches方法–>没有实现默认会将事件传递给上一个响应者–>找到上一个响应者–>找不到方法作废

    11. 理解单例设计模式

    一、单例模式的作用

    它可以保证某个类在运行过程中,只有一个实例,也就是对象实例只占用一份系统内存资源。

    二、单例的要点

    该类有且只有一个实例
    该类必须能自行创建这个实例
    该类必须能够向整个系统提供这个实例

    三、单例的优缺点

    • 优点:

    • 提供了唯一实例的受访对象

    • 因为在系统中只存在一个实例,在频繁访问和调用时,节省了系统创建和销毁资源的开销,提高系统性能。

    • 因为单例化的类,控制了实例化的过程,所以能更灵活修改实例化的过程。

    • 缺点:

    • 单例模式没有抽象层,不容易扩展。

    • 单例模式往往职责过重,一定程度上违背了“单一职责原则”。

    #import <Foundation/Foundation.h>
    @interface Class : NSObject
    + (instancetype)defaultManager;
    @end
    
    
    //重写init
    - (instancetype)init {
    
        @throw [NSException exceptionWithName:@"不允许调用Class" reason:@"因为Class是一个单例,只能通过default方法获获取对象" userInfo:nil];
    }
    
    //私有方法创建
    - (instancetype)initPrivate {
    
        if (self = [super init]) {
            
            //干一些事情
        }
        return self;
    }
    
    + (instancetype)defaultManager{
    
        static DataBaseManager * instance = nil;
        static dispatch_once_t onceToken = 0;
        dispatch_once(&onceToken, ^{
            if (!instance) {
                
                instance = [[Class alloc]initPrivate];
            }     
        });
        return instance;
    }
    

    Day--4

    12. 下面的代码输出什么?

    @implementation Son : Father
        - (id)init
        {
            self = [super init];
            if (self) {
                NSLog(@"%@", NSStringFromClass([self class]));
                NSLog(@"%@", NSStringFromClass([super class]));
            }
            return self;
        }
    @end
    
    2016-05-13 17:52:51.694 Test[12816:262134] Son
    2016-05-13 17:52:51.696 Test[12816:262134] Son
    

    原因:调用父类初始化的时候,是不看指针看对象的,因此谁调用super谁就是super的拥有者

    13. objc中的类方法和实例方法有什么本质区别和联系?

    类方法
    • 类方法属于类对象
    • 类方法只能通过类对象调用
    • 类方法中的self是类对象
    • 类方法可以调用其他类方法
    • 类方法中不能访问成员变量
    • 类方法不能直接调用对象方法
    实例方法
    • 实例方法是属于实例对象的
    • 实例方法只能呢通过实例对象调用
    • 实例方法中的self是实例对象
    • 实例方法中可以访问成员变量
    • 实例方法中直接调用实例方法
    • 实例方法中也可以调用类方法(通过类名)

    14. SDWebImage的原理

    1.入口
    setImageWithURL:placeholderImage:options: 会先把placeholderImage展示,然后 SDWebImageManager根据URL开始处理图片.

    2.进入
    SDWebImageManager-downloadWithURL:delegate:options:userInfo:,交给SDImageCache从缓存查找图片是否已经下载 :
    通过: queryDiskCacheForKey:delegate:userInfo:.

    3.先从内存图片缓存查找是否有图片,如果内存中已经有图片缓存SDWebImageDelegate回调imageCache:didFindImage:ForKey:userInfo: 到SDWebImageManager

    4.SDWebImageManagerDelegate回调

    webImageManager:didFinishWithImage: 
    

    到UIImageView+WebCache等前端展示图片

    5.如果内存缓存中没有,生成NSInvocationOperation添加到队列开始从硬盘查找图片是否已经缓存。

    6.根据 URLKey 在硬盘缓存目录下尝试读取图片文件.这一步是在 NSOperation 进行的操作,所以回主线程进行结果回调.

    7.如果上一操作从硬盘读取到了图片,将图片添加到内存缓存中,如果内存空间过小会先清空内存缓存.
    SDImageCacheDelegate回调 imageCache:didFindImage:forKey:userInfo: 进而回调展示图片.

    8.如果从硬盘缓存目录读取不到图片,说明所有缓存都不存在该图片,需要下载图片,回调imageCache:didNotFindImageForKey:userInfo:。

    9.共享或重新生成一个下载器 SDWebImageDownloder开始下载图片

    10.图片下载由 NSURLConnection来做,实现相关的Delegate来判断图片下载中,下载完成和下载失败.

    11.connection:didReceiveData:中利用 ImageIO 做了按图片下载进度加载效果。

    12.connectionDidFinishLoading: 数据下载完成后交给 SDWebImageDecoder 做图片解码处理。

    13.图片解码处理在一个 NSOperationQueue 完成,不会拖慢主线程 UI。如果有需要对下载的图片进行二次处理,最好也在这里完成,效率会好很多。

    14.在主线程notifyDelegateOnMainThreadWithInfo:宣告解码完成,imageDecoder:didFinishDecodingImage:userInfo:回调给 SDWebImageDownloader。

    15.imageDownloader:didFinishWithImage: 回调给 SDWebImageManager 告知图片下载完成。

    16.通知所有的 downloadDelegates 下载完成,回调给需要的地方展示图片。

    17.将图片保存到 SDImageCache 中,内存缓存和硬盘缓存同时保存。写文件到硬盘也在以单独 NSInvocationOperation 完成,避免拖慢主线程。

    18.SDImageCache 在初始化的时候会注册一些消息通知,在内存警告或退到后台的时候清理内存图片缓存,应用结束的时候清理过期图片。

    19.SDWI 也提供了 UIButton+WebCache 和 MKAnnotationView+WebCache,方便使用。

    20.SDWebImagePrefetcher 可以预先下载图片,方便后续使用

    15. BAD_ACCESS在什么情况下出现?如何调试BAD_ACCESS错误?

    什么是 EXC_BAD_ACCESS?

    不管什么时候当你遇到EXC_BAD_ACCESS这个错误,那就意味着你向一个已经释放的对象发送消息。访问了野指针,比如对一个已经释放的对象执行了release、访问已经释放对象的成员变量或者发消息。

    EXC_BAD_ACCESS的本质

    技术层面的解释有些复杂。在C和Objective-C中,你一直在处理指针。指针无非是存储另一个变量的内存地址的变量。当您向一个对象发送消息时,指向该对象的指针将会被引用。这意味着,你获取了指针所指的内存地址,并访问该存储区域的值。

    当该存储器区域不再映射到您的应用时,或者换句话说,该内存区域在你认为使用的时候却没有使用,该内存区域是无法访问的。 这时内核会抛出一个异常( EXC ),表明你的应用程序不能访问该存储器区域(BAD ACCESS) 。

    总之,当你碰到EXC_BAD_ACCESS ,这意味着你试图发送消息到的内存块,但内存块无法执行该消息。但是,在某些情况下, EXC_BAD_ACCESS是由被损坏的指针引起的。每当你的应用程序尝试引用损坏的指针,一个异常就会被内核抛出。

    调试EXC_BAD_ACCESS

    1.重写object的respondsToSelector方法,现实出现EXEC_BAD_ACCESS前访问的最后一个object
    2.通过 Zombie
    3.设置全局断点快速定位问题代码所在行
    4.Xcode 7 已经集成了BAD_ACCESS捕获功能:Address Sanitizer。 用法如下:在配置中勾选✅Enable Address Sanitizer

    16.什么是block,简述block实现原理

    • BLOCK是什么?

    • 苹果推荐的类型,效率高,在运行中保存代码。用来封装和保存代码,有点像函数,BLOCK可以在任何时候执行。

    • BOLCK和函数的相似性:

    • 可以保存代码

    • 有返回值

    • 有形参

    • 调用方式一样。

    • block类型:void(^)()

    • Block存储

    • Block默认存储在栈中,访问了外界的对象,不会对对象retain;如果对block进行一次copy,block就会存储在堆中,访问了外界的对象,会对对象进行retain操作。

    • Block的定义
      注意:

    • 如果block中没有使用外部变量,默认就是全局

    • 如果block中使用了外部变量,就是堆

    • Block可以访问局部变量,但是不能修改。

    • ARC中,默认局部变量是强引用

    block循环引用问题
    block尽量少使用self
    block尽量少使用下划线(_)直接访问成员属性
    要避免强引用到self的话,用__weak把self重新引用一下就行

    Block的实现

    我们所需要知道的是 block 就是一个对象,在它所在的内存中,保存着block自身的实现函数,可在调用block时用block自身的代码替代,同时保持着一个Block描述,标志着block的内存size与持有对象的指针。当声明与实现一个Block时,创建的闭包会捕获在它的域中的任何涉及的变量,通过在内存中持有他们,能够在block的实现中对其进行访问。在默认情况下,任何在block的域中被捕获的变量都不能被修改,除非这个变量已被给予了__block的标志。当block捕获了一个对象时,它会对其进行retain操作,并在block代码执行完毕完release对象,这样才能保证在block执行过程中,对象不会因引用计数为0而被释放掉。我们需要理解的是,block本身就是一个对象,它对其他对象的引用与一般的对象引用类似,都是需要对引用对象进行retain与release

    17. 写一个宏MIN,这个宏输入两个参数并返回较小的一个

    #define MIN(a,b)  ((a)>(b)?(b):(a))
    

    18.frame和bounds有什么不同?

    frame是在父视图坐标系下的位置和大小。bounds是自身坐标系下的位置和大小。

    frame以父控件的左上角为坐标原点,bounds以控件本身的左上角为坐标原点

    frame:以父控件左上角为原点。bounds:以自己的左上角为原点,bounds x,y永远为0(这是错误的认识)

    frame和bounds都是用来描述一块区域frame:描述可视范围,也就是说从左上角的0,0点开始延伸,它延伸的区域就是我们的可视范围

    bounds:描述可视范围在内容的区域,所有的子控件都有内容,它就类似于空气,是看不到的,正常情况下,内容是无限大的,所有的子控件其实都是放在内容上的,在可视范围内的内容我们才能 看见,所以正常情况下,内容的左上角(bounds)与可视范围(frame)的左上角是重合的,当修 改bounds的x与y都会导致子控件跟着移动.需要注意的是,可视范围(frame)是永远不会变的,它是相对父控件的.

    所有的子控件都是相对于内容bounds:修改内容原点

    相对性:可视范围相对于父控件位置永远不变 可视范围相对于内容,位置改变

    Day-5

    19.程序输出的结果

    int main(){
             int a[5] = {1, 2, 3, 4, 5};
             int * ptr = (int *)(&a + 1);
             printf("%d\n", *(a + 1));
             printf("%d", *(ptr - 1));
           return 0;
    }
    
    2
    5Program ended with exit code: 0
    

    1.*(a+1)=a[0+1]=a[1]=2
    2.&a表示对数组取地址,&a+1表示a[5]后面一个地址,
    (ptr-1)表示对当前数组元素地址向前移动一位,并取值,故等于a[4]=5

    20.谈谈RunLoop的简单理解

    一、RunLoop从字面意思上看:
    • 运行循环
    • 跑圈

    RunLoop的基本作用:

    • 保持程序的持续运行
    • 处理APP中各种事件(比如:触摸事件,定时器事件,Selector事件等)
    • 能节省CPU资源,提高程序的性能:该做事的时候就唤醒,没有事情就睡眠

    假如没有了RunLoop:

    • 大家都知道程序的入口是main函数:
    int main(int argc, char * argv[]) {
           @autoreleasepool { 
              return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
            }
    }
    
    • 如果没有RunLoop,程序就会在main函数执行完毕的时候退出,也正是因为有了RunLoop,导致main函数没有马上退出,保证了程序持续运行;

    • 其实是在UIApplicationMain函数内部启动了一个RunLoop;

    • 这个默认启动的RunLoop是跟主线程相关联的

    • RunLoop内部其实是有一个do-while循环(可以从RunLoop源码中找到),暂且可以理解为下面的代码:

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            BOOL running = YES;
            do {
                /**
                 * 在这里执行各种任务,处理事件
                 * 这个过程是持续运行
                 */  
            } while (running);
        }
        return 0;
    }
    
    二、RunLoop对象
    • iOS中有2套API来访问和使用
    • RunLoopFoundation框架中的NSRunLoop;
    • Core Foundation中的CFRunLoop;
    • NSRunLoop是基于CFRunLoopRef的一层OC包装,所以要了解RunLoop内部结构,需要多研究CFRunLoopRef层面的API(Core Foundation层面)
    三、RunLoop与线程
    • 每条线程都有唯一的一个与之对应的RunLoop对象

    • 主线程中的RunLoop由系统自动创建,子线程中RunLoop可以通过手动创建

    • RunLoop在线程结束的时候会被销毁

    • 获取RunLoop对象

    • Foundation框架中

    [NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
    [NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
    
    • Core Foundation框架中
    CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
    CFRunLoopGetMain(); // 获得主线程的RunLoop对象
    

    21.找错误

    试题1:

    char * GetMemory(void){
        char p[] = "Hello World";
        return p;
    }
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
                char * str = NULL;
                str = GetMemory();
                printf("%s\n",str);
        }
        return 0;
    }
    
    错误为:
    char p[] = "Hello World";
    return p;//不能返回栈地址,因为栈空间自动释放,应该返回栈空间的数据
    正确应该为:
    char *p = "Hello World";
    

    试题2:

    void GetMemory(char **p, int num){ 
          *p = (char *)malloc(num);
    }
    void Test(void){
         char * str = NULL; 
         GetMemory(&str, 100); 
         strcpy(str, "Hello"); 
         printf(str);
    }
    
          // 堆空间需要手动释放,申请了堆内存时也要判断是否申请成功
          char * str = NULL;
          GetMemory(&str, 100); 
          if(str) {
               strcpy(str, "Hello"); 
               printf("%s",str); 
               free(str); 
               str = NULL; 
          }
    

    Day-6

    22. 写出下列两个属性的Setter方法

    @property (nonatomic, retain) NSString * name;
    @property (nonatomic, copy) NSString * name;
    
    - (void) setName:(NSString *) name {
             if( _name != name) { [
                  _name release]; 
                  _name = [name retain]; 
              }
    }
    - (void) setName:(NSString *) name { 
                 _name = [name copy];  
    }
    

    retain修饰的属性setter方法的实现步骤:
    1:判断新值与旧值是否相等,如果不等执行以下操作
    2:将旧值执行一次release操作(旧值release)
    3:再将新值执行一次retain操作再赋给旧值(新值retain再赋值)

    copy修饰的属性:如果要保证返回的是一个不可变的版本就要将新值执行一次copy操作

    23. 类别和继承什么区别

    区别:

    • 1.类别是对方法的扩展,不能添加成员变量。继承可以在原来父类的成员变量的基础上,添加新的成员变量
    • 2.类别只能添加新的方法,不能修改和删除原来的方法。继承可以增加、修改和删除方法。
    • 3.类别不提倡对原有的方法进行重载。继承可以通过使用super对原来方法进行重载。
    • 4.类别可以被继承,如果一个父类中定义了类别,那么其子类中也会继承此类别。

    24. 线程与进程的区别和联系?

    • 进程: 进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时,它才能成为一个活动的实体,我们称其为进程。进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。它可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体。它不只是程序的代码,还包括当前的活动,通过程序计数器的值和处理寄存器的内容来表示。

    • 线程: 通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源。在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度。

    • 线程与进程的区别:

    • a.地址空间和其它资源:进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。

    • b.通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。

    • c.调度和切换:线程上下文切换比进程上下文切换要快得多。

    • d.在多线程OS中,进程不是一个可执行的实体

    相关文章

      网友评论

      本文标题:我的iOS面试题

      本文链接:https://www.haomeiwen.com/subject/normrttx.html