美文网首页
iOS面试题汇总---基础

iOS面试题汇总---基础

作者: Mr_MayBee | 来源:发表于2017-11-15 21:52 被阅读52次

    基础

    main方法之前都发生了啥

    dyld开始将程序二进制文件初始化
    交由ImageLoader读取image,其中包含了我们的类、方法等各种符号
    由于runtime向dyld绑定了回调,当image加载到内存后,dyld会通知runtime进行处理
    runtime接手后调用map_images做解析和处理,接下来load_images中调用call_load_methods方法,遍历所有加载进来的Class,按继承层级依次调用Class的load方法和其Category的load方法

    请解释以下关键词的区别:assign VS weak,__block VS __weak

    消息转发的几个步骤

    
    方案一:
    
    + (BOOL)resolveInstanceMethod:(SEL)sel
    
    + (BOOL)resolveClassMethod:(SEL)sel
    
    方案二:
    
    - (id)forwardingTargetForSelector:(SEL)aSelector
    
    方案三:
    
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
    
    - (void)forwardInvocation:(NSInvocation *)anInvocation;
    
    

    如何实现单例,单例会有什么弊端?

    
    // OC版
    
    + (instancetype)sharedInstance
    
    {
    
    static id sharedInstance = nil;
    
    static dispatch_once_t onceToken;
    
    dispatch_once(&onceToken, ^{
    
    sharedInstance = [[self alloc] init];
    
    });
    
    return sharedInstance;
    
    }
    
    // Swift版
    
    static let sharedInstance : <#SingletonClass#> = <#SingletonClass#>()
    

    缺点是会占着内存 直至程序结束

    NSURLConnection和NSURLSession共同点和区别

    1.下载
    NSURLSession针对下载进行了拆分,分为NSURLSessionDataTask, NSURLSessionUploadTask和NSURLSessionDownloadTask,需要主动resume才能执行。
    服务器返回小体积数据的时候或执行上传任务,二者区别不大
    2.下载任务方式
    NSURLConnection的原理是先把文件下载到内存,再写入沙盒,容易造成内存暴涨甚至崩溃。
    NSURLSession则是先把文件下载temp临时文件夹中,下载完成后默认删除temp文件夹中的临时文件,需要开发者在完成回调中主动保存
    3.请求方法控制
    NSURLConnection会实例化对象,实例化完成默认开始,只提供了cancel方法,取消后需要重新下载整个文件
    NSURLSession则提供了start,suspend,resume等方法,暂停后可以恢复当前下载请求
    4.断点续传
    NSURLConnection要设置header中的range属性开始运行循环。通过代理方法获取事件源,下载一部分就会持续调用代理方法,并使用NSOutputStream进行数据保存
    NSURLSession暂停后可以通过cancelByProducingResumeData方法获取到当前下载文件,需要恢复下载的时候调用downloadTaskWithResumeData进行断点续传
    5.可配置信息
    NSURLConnection依赖于一个全局的配置对象,不能进行单个请求的灵活配置
    NSURLSession可以通过NSURLSessionConfiguration类进行cookie,安全和高速缓存策略,最大主机连接数,资源管理,网络超时等配置
    

    沙盒的目录结构是怎样的?各自一般用于什么场合?

    Notification的使用场景是什么?同步还是异步?在什么线程执行?

    手写字符串setter以及getter代码

    用@property声明的字符串为什么用copy关键字以及如果改用strong关键字,可能造成什么问题?

    因为父类指针可以指向子类对象,使用 copy 的目的是为了让本对象的属性不受外界影响,使用 copy 无论给我传入是一个可变对象还是不可对象,我本身持有的就是一个不可变的副本.
    如果我们使用是 strong,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性.

    关键字 注释
    浅复制(shallow copy)   在浅复制操作时,对于被复制对象的每一层都是指针复制。
    深复制(one-level-deep copy)    在深复制操作时,对于被复制对象,至少有一层是深复制。
    完全复制(real-deep copy)    在完全复制操作时,对于被复制对象的每一层都是对象复制。
    非集合类对象的 copy 与 mutableCopy
    
    [不可变对象 copy] // 浅复制 
    [不可变对象 mutableCopy] //深复制
    [可变对象 copy] //深复制 
    [可变对象 mutableCopy] //深复制
    类对象的 copy 与 mutableCopy
    
    [不可变对象 copy] // 浅复制 
    [不可变对象 mutableCopy] //单层深复制
    [可变对象 copy] //单层深复制
    [可变对象 mutableCopy] //单层深复
    这里需要注意的是集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制。
    

    MD5和Base64的区别是什么,各自场景是什么?

    描述快速排序 冒泡排序 二分查找 等算法逻辑(二维有序数组查找数字)

    SDWebImage里面给UIImageView加载图片的逻辑是什么样的?

    对runtime的理解以及message send如果寻找不到相应的对象,会如何进行后续处理

    同一方法方法在类 父类 扩展中同时实现 会执行哪个 为什么

    说说load方法和initialize方法的异同 (执行时间 各自用途 会不会调用父类)

    +(void)load;当类对象被引入项目时, runtime 会向每一个类对象发送 load 消息load 方法会在每一个类甚至分类被引入时仅调用一次,调用的顺序:父类优先于子类, 子类优先于分类load 方法不会被类自动继承
    
    +(void)initialize;也是在第一次使用这个类的时候会调用这个方法
    

    实现一个第三方控件,可以在任何时候出现在APP界面最上层

    添加一个优先级高的window 如果只有一个window也可以直接添加view然后调高此view的z轴位置

    UIView和CALayer区别和联系

    UIView显示在屏幕上归功于CALayer,通过调用drawRect方法来渲染自身的内容,调节CALayer属性可以调整UIView的外观,UIView继承自UIResponder,CALayer不可以响应用户事件
    
    UIView是iOS系统中界面元素的基础,所有的界面元素都继承自它。它内部是由Core Animation来实现的,它真正的绘图部分,是由一个叫CALayer(Core Animation
    
    Layer)的类来管理。UIView本身,更像是一个CALayer的管理器,访问它的根绘图和坐标有关的属性,如frame,bounds等,实际上内部都是访问它所在CALayer的相关属性
    
    UIView有个layer属性,可以返回它的主CALayer实例,UIView有一个layerClass方法,返回主layer所使用的类,UIView的子类,可以通过重载这个方法,来让UIView使用不同的CALayer来显示
    

    如何减小一个应用程序的尺寸?

    如何提高一个性用程序的性能?

    不同版本的APP,数据库结构变化了,如何处理?

    如何适配不同尺寸的屏幕以及新系统(iOS 10和iOS 11适配经验)

    分析下应用的页面结构、约束或者 frame 布局的连法和计算方法

    从打开程序到获取devicetoken的过程中发生了什么?

    用户的设备会于苹果APNS服务器形成一个长连接,用户设备会发送uuid和Bundle

    idenidentifier给苹果服务器,苹果服务器会加密生成一个deviceToken给用户设备,然后设备会将deviceToken发送给APP的服务器,服务器会将deviceToken存进他们的数据库,这时候如果有人发送消息给我,服务器端就会去查询我的deviceToken,然后将deviceToken和要发送的信息发送给苹果服务器,苹果服务器通过deviceToken找到我的设备并将消息推送到我的设备上,这里还有个情况是如果APP在线,那么APP服务器会于APP产生一个长连接,这时候APPF服务器会直接通过deviceToken将消息推送到设备上

    所熟知的几种排序方式以及他们的时间空间复杂度

    如何处理多个网络请求并发的情况(多线程阻断)

    不用临时变量实现swap(a,b)(加法或者异或)

    a = a ^ b;
    b = a ^ b;
    a = a ^ b;
    

    可以这么理解

    c = a ^ b; 此时 c 是a 和b的异或
    b = c ^ b; 此时的b已经是原来的a了
    a = c ^ b;再次异或 是通过c和初始的a获取原来的b 完成交换
    

    为什么代理要用weak?代理的delegate和dataSource有什么区别?block和代理的区别?

    1)weak修饰代理属性,指明该对象并不负责保持delegate这个对象。delegate这个对象的销毁又外部控制

    (2)weak和assign的不同点:weak策略在属性所指的对象遭到销毁时,系统会将weak修饰的属相对象的指针置为nil,在OC给nil发消息是不会有什么问题的。如果是用assign,在属性所指的对象销毁之后,属性对象的指针并为置为nil,造成野指针的存在。这个时候再次给此对象发送消息,极易造成崩溃问题。assign可以用于修饰非OC对象,而weak必须勇于OC对象。

    (3)block和代理的区别:

    block更轻型,是用也更简单,能够直接访问上下文,类中不需要存储临时数据,使用block,代码连贯,易读。delegate需要实现接口,与方法实现进行分离,需要存储一些临时数据。代码比较分离,不易读。但是可以在很大程度上实现代码的解耦。

    block根据使用分类,有三种情形:

    a.临时性的,只用在栈中,不会存储起来

    b.需要存储起来,但只会调用一次,或者一个完成时期。比如一个UIView动画,动画完成之后,需要使用block通知外面。一旦调用block之后,这个block就可以删除掉

    c.需要存储起来,可能会调用多次。比如按钮的点击事件,假如采用block实现,这种block就需要长期存储,并且会调用多次。调用之后block也会删除,可能还有下一次按钮的点击。

    对于临时性的,只在栈中使用的block,没有循环引用的问题。block会自动释放掉。而只调用一次的block,就需要看内部的实现了。正确的实现时block调用之后,吗上赋值为空。这样block也会释放掉,同样也不会引起循环引用。

    多次调用的block,极易引起循环引用,使用时需要注意

    属性的实质是什么?包括哪几个部分?属性默认的关键字都有哪些?@dynamic关键字和@synthesize关键字是用来做什么的?

    属性的实质是@property = ivar + getter + setter;

    属性的默认关键字是什么?

    基本数据:  atomic,readwrite,assign
    普通的 OC 对象: atomic,readwrite,strong
    

    @property有两个对应的词,一个是@synthesis一个是@dynamic。若两者都没有写,默认的就是:@syntheszie var = _var;

    @synthesis的语义是如果你没有手动实现setter和getter方法,那么编译器会自动为你加上这两个方法
    @dynamic告诉编译器:属性的setter和getter方法由开发者自己实现,不需要自动生成。(对于readonly的属性只需提供getter即可)加入一个属性被声明@dynamic var;然后你也没有提供@setter和@getter方法,可以通过编译,但是运行的时候,程序运行至instance.var = someVar会因为缺少setter方法而crash。同样如果缺失getter方法,程序运行至someVar = instance.var时导致crash

    如何令自己所写的对象具有拷贝功能?

    若想令自己所写的对象具有拷贝功能,则需实现 NSCopying 协议。如果自定义的对象分为可变版本与不可变版本,那么就要同时实现 NSCopyiog 与NSMutableCopying 协议,不过一般没什么必要,实现 NSCopying 协议就够了
    

    可变集合类 和 不可变集合类的 copy 和 mutablecopy有什么区别?如果是集合是内容复制的话,集合里面的元素也是内容复制么?

    使用copy时 可变集合的指针地址以及内存地址都不相同 深复制 不可变集合的指针地址不一样但是内存地址一样 属于浅复制

    使用mutableCopy的时候无论是可变集合还是不可变集合的指针地址和内存地址都不同 都属于深复制

    为什么IBOutlet修饰的UIView也适用weak关键字?

    我们将控件拖到Storyboard上,相当于创建了一个对象,而这个对象是加到试图控制器的view上,存放在view的subviews数组中。即我们的控件对象是属于的view的,view对其子控件之前的关系是强引用。当我们使用Outlet属性的时候,这个Outlet属性是有view来进行强引用的。我们是在viewController中仅仅对其进行使用,没有必要拥有它,所以使用weak进行修饰。

    nonatomic和atomic的区别?atomic是绝对的线程安全么?为什么?如果不是,那应该如何实现?

    nonatomic和atomic用来决定编译器生成的getter和setter操作是否为原子操作。atomic不是绝对的线程安全。atomic的本意是指属性的存取方法是线程安全的,并不保证整个对象是线程安全的。要保证多线程安全的话要使用互斥锁才能解决。

    13、进程和线程的区别?同步异步的区别?并行和并发的区别?

    14、线程间通信?

    15、GCD的一些常用的函数?(group,barrier,信号量,线程同步)

    16、如何使用队列来避免资源抢夺?

    数据持久化的几个方案(fmdb用没用过)

    FMDB、Core Data、NSUserDefaults、plist、NSKeyedArchiver(对象归解档)

    18、说一下AppDelegate的几个方法?从后台到前台调用了哪些方法?第一次启动调用了哪些方法?从前台到后台调用了哪些方法?

    19、NSCache优于NSDictionary的几点?

    20、知不知道Designated Initializer?使用它的时候有什么需要注意的问题?

    21、实现description方法能取到什么效果?

    22、objc使用什么机制管理对象内存?

    block的实质是什么?一共有几种block?都是什么情况下生成的?

    Clang(LLVM编译器)将含有Block语法的源代码转换为我们可读源代码的功能。通过“-rewrite-objc”选项就能将含有Block语法的源代码变换为C++的源代码(本质是使用了struct结构的C语言源代码)
    而Block实质上就是Objective-C对象;
    根据isa指针,block一共有3种类型的block

    _NSConcreteGlobalBlock 全局静态
    _NSConcreteStackBlock 保存在栈中,出函数作用域就销毁
    _NSConcreteMallocBlock 保存在堆中,retainCount == 0销毁而ARC和MRC中,还略有不同;
    

    为什么在默认情况下无法修改被block捕获的变量? __block都做了什么?

    Block只捕获Block中会用到的变量。由于只捕获了自动变量(自动变量是以值传递方式传递到Block的构造函数里面)的值,并非内存地址,所以Block内部不能改变自动变量的值。Block捕获的外部变量可以改变值的是静态变量,静态全局变量,全局变量。

    Runtime

    objc在向一个对象发送消息时,发生了什么?

    1.OC 在向一个对象发送消息时,runtime 库会根据对象的 isa指针找到该对象对应的类或其父类中查找方法。。
    2.注册方法编号(这里用方法编号的好处,可以快速查找)。
    3.根据方法编号去查找对应方法。
    4.找到只是最终函数实现地址,根据地址去方法区调用对应函数。
    

    什么时候会报unrecognized selector错误?iOS有哪些机制来避免走到这一步?

    首先对象查找selector时,先查找cachelist,如果没有则查找methodlist,如果还没有就查找父类的methodlist,都没有的时候_objc_msgForward会尝试做消息转发,此时还有三次机会处理这次selector访问
    
    1. + (BOOL)resolveInstanceMethod:(SEL)sel; 允许用户在此时为该Class动态添加实现。如果有实现了,则调用并返回。如果仍没实现,继续下面的动作。
    
    2. - (id)forwardingTargetForSelector:(SEL)aSelector尝试找到一个能响应该消息的对象。如果获取到,则直接转发给它。如果返回了nil,继续下面的动作。
    
    3. - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector; 尝试获得一个方法签名。如果获取不到,则直接调用doesNotRecognizeSelector抛出异常。如果获取到了,就会产生一个NSInvocation给第二个方法 - (void)forwardInvocation:(NSInvocation *)anInvocation; 在anInvocation中有target和selector,可以修改,然后让target进行调用,不调用也可以,只是什么都不发生
    
    如果以上三次机会都不把握,则会调用- (void)doesNotRecognizeSelector:(SEL)aSelector; 默认的实现是抛出异常
    

    能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?

    编译后的类已经注册在runtime中,类结构体中的objc_ivar_list实例变量的链表和instance_size实例变量的内存大小已经确定,runtime会调用class_setvarlayout或class_setWeaklvarLayout来处理strong weak引用.所以不能向存在的类中添加实例变量
    运行时创建的类是可以添加实例变量,调用class_addIvar函数.但是的在调用objc_allocateClassPair之后,objc_registerClassPair之前,原因同上.
    

    runtime如何实现weak变量的自动置nil?

    runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会 dealloc, 在这个 weak 表中搜索,找到所有以a为键的 weak 对象,从而设置为 nil。
    weak 修饰的指针默认值是 nil (在Objective-C中向nil发送消息是安全的)

    给类添加一个属性后,在类结构体里哪些元素会发生变化?

    class object/metaclass
    objc_class的定义

    struct objc_class {
        Class isa  OBJC_ISA_AVAILABILITY;
    
    #if !__OBJC2__
        Class super_class                                        OBJC2_UNAVAILABLE;
        const char *name                                         OBJC2_UNAVAILABLE;
        long version                                             OBJC2_UNAVAILABLE;
        long info                                                OBJC2_UNAVAILABLE;
        long instance_size                                       OBJC2_UNAVAILABLE;
        struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;
        struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;
        struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
        struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
    #endif
    
    } OBJC2_UNAVAILABLE;
    /* Use `Class` instead of `struct objc_class *` */
    

    创建一个类属性很简单,主要有以下几个步骤:

    使用@property (class)来声明一个类属性;
    为类属性创建一个存储变量,通常为全局变量;
    实现类属性的getter与setter方法,如果是只读属性,只需要实现getter方法。
    

    类相当于一个对象, 当对象执行这个方法即会发送一条消息, 之后根据isa指针查找消息对象,这时会在methodLists中遍历,如果cache了,常用的方法调用时就能够提高调用的效率。

    类结构
    1、isa指针?(对象的isa,类对象的isa,元类的isa都要说)

    类方法和实例方法有什么区别?

    类方法,也称静态方法,指的是用static关键字修饰的方法。此方法属类本身的方法,不属于类的某一个实例(对象)。类方法中不可直接使用实例变量。其调用方式有三种:可直接调用、类名.方法名、对象名.方法名。实例方法指的是不用static关键字修饰的方法。每个实例对象都有自身的实例方法,互相独立,不共享一个。其调用方式只能是对象名.方法名。

    instance method 以减号 "-" 开头
    class method 以加号 “+” 开头,相当于static方法
    

    区别:
    静态方法在程序开始时生成内存,实例方法在程序运行中生成内存,
    所以静态方法可以直接调用,实例方法要先成生实例,通过实例调用方法,静态速度很快,但是多了会占内存。
    静态内存是连续的,因为是在程序开始时就生成了,而实例申请的是离散的空间,所以当然没有静态方法快,
    而且静态内存是有限制的,太多了程序会启动不了。

    使用场景:
    如果需要访问或者修改某个实例的成员变量时,将该方法定义成实例方法。
    类方法正好相反,它不需要访问或者修改某个实例的成员变量。
    类方法一般用于实现一些工具方法,比如对某个对象进行扩展,或者实现单例。

    类方法常驻内存,实例方法不是,所以类方法效率高但占内存。
    类方法在堆上分配内存,实例方法在堆栈上。
    事实上所有的方法都不可能在堆或者堆栈上分配内存,方法作为代码是被加载到特殊的代码内存区域,这个内存区域是不可写的。
    实例方法需要先创建实例才可以调用,比较麻烦,类方法不用,比较简单。

    事实上如果一个方法与他所在类型的实例无关,那么它就应该是静态的,决不会有人把它写成实例方法。所以所有的实例方法都与实例有关,既然与实例有关,那么创建实例就是必然的步骤,没有麻烦简单一说。实际上上你可以把所有的实例方法都写成静态的,将实例作为参数传入即可

    介绍一下分类,能用分类做什么?内部是如何实现的?它为什么会覆盖掉原来的方法?

    类别(Category)主要有3个作用:

    将类的实现分散到多个不同文件或多个不同框架中。
    创建对私有方法的前向引用。
    向对象添加非正式协议。
    

    声明:@interface 类名(分类名称) @end
    实现:@implementation 类名(分类名称) @end

    注意:
    (1)在分类只能增加方法,不能增加成员变量,如果要增加成员变量的话该考虑用继承去实现
    (2)在分类实现方法中可以访问类中的成员变量但是不能访问类中的属性@property
    (3)在分类中可以重新实现原类中的方法,但会将原类中的方法覆盖而失效。

    因为在执行对象成员方法的时候会优先去分类中查找,然后再去原类中去查找,最后去父类中去查找
    

    (4)如果一个类有多个分类,而且分类中有同名的方法那么最后编译的分类会将前面编译的分类覆盖而执行输出

    运行时能增加成员变量么?能增加属性么?如果能,如何增加?如果不能,为什么?

    可以添加属性的,但必须我们实现它的getter和setter方法。但是没有添加带下滑线同名的成员变量
    但是我们使用runtime我们就可以实现添加成员变量方法
    关键代码

    objc_setAssociatedObject(self, @selector(name), name,   OBJC_ASSOCIATION_COPY_NONATOMIC);
    return objc_getAssociatedObject(self, @selector(name));
    

    5、objc中向一个nil对象发送消息将会发生什么?(返回值是对象,是标量,结构体)

    高级

    UITableview的优化方法(缓存高度,异步绘制,减少层级,hide,避免离屏渲染)

    缓存高度:当我们创建frame模型的时候,计算出来cell的高度的时候,我们可以将cell的高度缓存到字典里面,以cell的indexpath和Identifier作为为key。
    异步绘制、减少层级:目前还不是很清楚
    hide:个人理解应该是hidden吧,把可能会用到的控件都创建出来,根据不同的情况去隐藏或者显示出来。
    避免离屏渲染:只要不是同时使用边框/边框颜色以及圆角的时候,都可以使用layer直接设置。不会造成离屏渲染。
    正确使用reuseIdentifier来重用Cells
    尽量使所有的view opaque,包括Cell自身
    尽量少用或不用透明图层
    如果Cell内现实的内容来自web,使用异步加载,缓存请求结果
    减少subviews的数量
    在heightForRowAtIndexPath:中尽量不使用cellForRowAtIndexPath:,如果你需要用到它,只用一次然后缓存结果
    尽量少用addView给Cell动态添加View,可以初始化时就添加,然后通过hide来控制是否显示

    2、有没有用过运行时,用它都能做什么?(获取属性列表,获取方法列表,交换方法,创建类,给新创建的类增加方法,改变isa指针)

    3、看过哪些第三方框架的源码?都是如何实现的?(如果没有,问一下多图下载的设计)

    4、SDWebImage的缓存策略?

    5、AFN为什么添加一条常驻线程?

    KVO的使用?实现原理?(为什么要创建子类来实现)

    KVO 提供一种机制,指定一个被观察对象(例如 A 类),当对象某个属性(例如 A 中的字符串 name)发生更改时,对象会获得通知,并作出相应处理;【且不需要给被观察的对象添加任何额外代码,就能使用 KVO 机制】

    当观察某对象 A 时,KVO 机制动态创建一个对象A当前类的子类,并为这个新的子类重写了被观察属性 keyPath 的 setter 方法。setter 方法随后负责通知观察对象属性的改变状况。

    KVC的使用?实现原理?(KVC拿到key以后,是如何赋值的?知不知道集合操作符,能不能访问私有属性,能不能直接访问_ivar)

    KVC(Key-value coding)键值编码,顾名思义。额,简单来说,是可以通过对象属性名称(Key)直接给属性值(value)编码(coding)“编码”可以理解为“赋值”。这样可以免去我们调用getter和setter方法,从而简化我们的代码,也可以用来修改系统控件内部属性

    赋值原理:

    (1)去模型中查找有没有setIcon方法,就直接调用这个set方法,给模型这个属性赋值[self setIcon:dict[@"icon"]];
    (2)如果找不到set方法,接着就会去寻找有没有icon属性,如果有,就直接访问模型中icon = dict[@"icon"];
    (3)如果找不到icon属性,接着又会去寻找_icon属性,如果有,直接_icon = dict[@"icon"];
    (4)如果都找不到就会报错
    [<Flag 0x7fb74bc7a2c0> setValue:forUndefinedKey:]

    相关文章

      网友评论

          本文标题:iOS面试题汇总---基础

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