ios相关共享学习准备

ios 招聘常见面试题+答案 基础篇

2018-08-25  本文已影响351人  帝步凡

1、weak和unowned

  共同点: 
  都可以修饰对象类型的property, 不会增加其引用计数.

  不同点: 
  1.出现时期, assign出现在ARC之前, weak出现在ARC之后; 
    2.可修饰的类型, assign既可以修饰对象, 又可以修饰基本类型, 而                        weak只能修饰对象; 
      3.安全性, assign修饰的对象在被销毁之后, 其指针依然存在, 容易导致野指针错误, 而weak修饰的对象销毁之后指针自动设置为nil, 不会发生危险.

  总结: 
  在ARC时代, 应该用weak来表示弱引用, 比如代理对象, 而用assign来修饰基本数据类型, 比如int, float, double. 最后建议, 为了兼容64位CPU, 尽量避免使用C数据类型, 而应该使用Objective-C新增的数据类型, 如NSInteger, NSUInteger, CGFloat等等.

2.static 关键字的作用

1:static关键字修饰局部变量:1:当static关键字修饰局部变量时,该局部变量只会初始化一次,在系统中只有一份内存   2:static关键字不可以改变局部变量的作用域,但是可延长局部变量的生命周期,该变量直到整个项目结束的时候才会被销毁

  2:static修饰的全局变量:作用域仅限于当前文件,外部类不可以访问到该变量

    3:extern:引用关键字,当某一个全局变量,没有用static修饰时,其作用域为整个项目文件,若是在其他类想引用该变量,则用extern关键字,例如,想引用其他类的全局变量,int age = 10;则在当前类中实现,extern int age;也可以在外部修改该变量,extern int age = 40;,若某个文件中的全局变量不想被外界修改,则用static修饰该变量,则其作用域只限于该文件

3.Objective-C如何对内存管理的,说说你的看法和解决方法?

1. (Garbage Collection)自动内存计数:这种方式和java类似,在你的程序的执行过程中。始终有一个高人在背后准确地帮你收拾垃圾,你不用考虑它什么时候开始工作,怎样工作。你只需要明白,我申请了一段内存空间,当我不再使用从而这段内存成为垃圾的时候,我就彻底的把它忘记掉,反正那个高人会帮我收拾垃圾。遗憾的是,那个高人需要消耗一定的资源,在携带设备里面,资源是紧俏商品所以iPhone不支持这个功能。所以“Garbage Collection”不是本入门指南的范围,对“Garbage Collection”内部机制感兴趣的同学可以参考一些其他的资料,不过说老实话“Garbage Collection”不大适合适初学者研究。

    解决: 通过alloc – initial方式创建的, 创建后引用计数+1, 此后每retain一次引用计数+1, 那么在程序中做相应次数的release就好了.

  2. (Reference Counted)手动内存计数:就是说,从一段内存被申请之后,就存在一个变量用于保存这段内存被使用的次数,我们暂时把它称为计数器,当计数器变为0的时候,那么就是释放这段内存的时候。比如说,当在程序A里面一段内存被成功申请完成之后,那么这个计数器就从0变成1(我们把这个过程叫做alloc),然后程序B也需要使用这个内存,那么计数器就从1变成了2(我们把这个过程叫做retain)。紧接着程序A不再需要这段内存了,那么程序A就把这个计数器减1(我们把这个过程叫做release);程序B也不再需要这段内存的时候,那么也把计数器减1(这个过程还是release)。当系统(也就是Foundation)发现这个计数器变成了0,那么就会调用内存回收程序把这段内存回收(我们把这个过程叫做dealloc)。顺便提一句,如果没有Foundation,那么维护计数器,释放内存等等工作需要你手工来完成。

      解决:一般是由类的静态方法创建的, 函数名中不会出现alloc或init字样, 如[NSString string]和[NSArray arrayWithObject:], 创建后引用计数+0, 在函数出栈后释放, 即相当于一个栈上的局部变量. 当然也可以通过retain延长对象的生存期.

    3. (NSAutoRealeasePool)内存池:可以通过创建和释放内存池控制内存申请和回收的时机.

        解决:是由autorelease加入系统内存池, 内存池是可以嵌套的, 每个内存池都需要有一个创建释放对, 就像main函数中写的一样. 使用也很简单, 比如[[[NSString alloc]initialWithFormat:@”Hey you!”] autorelease], 即将一个NSString对象加入到最内层的系统内存池, 当我们释放这个内存池时, 其中的对象都会被释放.

4.内存管理的几条原则时什么?按照默认法则.那些关键字生成的对象需要手动释放?在和property结合的时候怎样有效的避免内存泄露?

     当使用new、alloc或copy方法创建一个对象时,该对象引用计数器为1。如果不需要使用该对象,可以向其发送release或autorelease消息,在其使用完毕时被销毁。
      
      如果通过其他方法获取一个对象,则可以假设这个对象引用计数为1,并且被设置为autorelease,不需要对该对象进行清理,如果确实需要retain这个对象,则需要使用完毕后release。

       如果retain了某个对象,需要release或autorelease该对象,保持retain方法和release方法使用次数相等。

      使用new、alloc、copy关键字生成的对象和retain了的对象需要手动释放。设置为autorelease的对象不需要手动释放,会直接进入自动释放池。

5.NSOperation queue?
https://blog.csdn.net/chenyufeng1991/article/details/50281515

6.什么是延迟加载?
https://www.jianshu.com/p/665f6dbf38ff

7.BAD_ACCESS在什么情况下出现?

1.访问了野指针的时候,比如一个已经释放的对象执行了release、访问已经释放对象的成员变量或者发消息。
  2.死循环的时候也会。

8、0x8badf00d表示是什么?

Termination Reason: Namespace SPRINGBOARD, Code 0x8badf00d.
异常代码 0x8badf00d 指示应用程序已终止的 iOS 因为看门狗超时发生。应用程序时间太长,启动、 终止,或对系统事件作出响应。一个常见的原因做在主线程上的同步联网。无论操作是线程 0 上: 需要搬到后台线程,或处理方式不同,所以,它不会阻止在主线程

9.GCD与NSOperation这两者有什么区别?

GCD是基于c的底层api,NSOperation属于objection-c类。ios 首先引入的是NSOperation,IOS4之后引入了GCD和NSOperationQueue并且其内部是用gcd实现的。

相对于GCD:

1,NSOperation拥有更多的函数可用,具体查看api。 
  2,在NSOperationQueue中,可以建立各个NSOperation之间的依赖关系。 
    3,有kvo,可以监测operation是否正在执行(isExecuted)、是否结束(isFinished),是否取消(isCanceld)。 
      4,NSOperationQueue可以方便的管理并发、NSOperation之间的优先级。 


GCD主要与block结合使用。代码简洁高效。 
GCD也可以实现复杂的多线程应用,主要是建立个个线程时间的依    赖关系这类的情况,但是需要自己实现相比NSOperation要复杂。 
具体使用哪个,依需求而定。 从个人使用的感觉来看,比较合适的用法是:除了依赖关系尽量使用GCD,因为苹果专门为GCD做了性能上面的优化。

10.单例的优弊是什么?

优点:

1:一个类只被实例化一次,提供了对唯一实例的受控访问。

  2:节省系统资源

    3:允许可变数目的实例。



缺点:

1:一个类只有一个对象,可能造成责任过重,在一定程度上违背了“单一职责原则”。

  2:由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。

    3:滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。

11.nonatomic和atomic对比 说说你对他们的理解。
https://www.jianshu.com/p/270239034d65

12.两个对象之间相互通信 有哪些通信方式

delegate:代理是一种设计模式,它是通过被代理者定义[代理协议](https://www.baidu.com/s?wd=%E4%BB%A3%E7%90%86%E5%8D%8F%E8%AE%AE&tn=SE_PcZhidaonwhc_ngpagmjz&rsv_dl=gh_pc_zhidao)[委托代理](https://www.baidu.com/s?wd=%E5%A7%94%E6%89%98%E4%BB%A3%E7%90%86&tn=SE_PcZhidaonwhc_ngpagmjz&rsv_dl=gh_pc_zhidao)者实现协议,用于两个对象间的通信交互。在 IOS 中 delegate 主要用于视图与使用对象之间的通信交互,delegate 的效率是最高的,典型的特就是它有返回值。

block:block 类似与函数,可作为参数进行传递用于回调,block 可以定义在方法里,函数不能。block 语法简单,写在方法里可以访问局部变量可以使代码更加的紧凑,结构化。相对于 delegate,block 不用建立[代理协议](https://www.baidu.com/s?wd=%E4%BB%A3%E7%90%86%E5%8D%8F%E8%AE%AE&tn=SE_PcZhidaonwhc_ngpagmjz&rsv_dl=gh_pc_zhidao),使用简单。

通知:NSnotification 一个中心对象注册和发送通知,所用的其他的对象都可以收到通知。

KVC:键-值编码是用于间接访问对象属性的机制,并不需要调用 set 或者 get 方法访问变量,是通过 set value for key 进行间接访问[实例变量](https://www.baidu.com/s?wd=%E5%AE%9E%E4%BE%8B%E5%8F%98%E9%87%8F&tn=SE_PcZhidaonwhc_ngpagmjz&rsv_dl=gh_pc_zhidao)。

13.os的数据存储有哪些方式

1.数据存储方式:
  1.1 NSKeyedArchiver:采用归档的形式来保存数据,该数据对象需要遵守NSCoding协议,并且该对象对应的类必须提供encodeWithCoder:和initWithCoder:方法。前⼀一个方法告诉系统怎么对对象进行编码,而后⼀一个方法则是告诉系统怎么对对象进行解码。
    1.2 NSUserDefaults:用来保存应用程序设置和属性、用户保存的数据。用户再次打开程序或开机后这些数据仍然存在。NSUserDefaults可以存储的数据类型包括:NSData、NSString、NSNumber、NSDate、NSArray、NSDictionary。如果要存储其他类型,则需要转换为前面的类型,才能用NSUserDefaults存储。
      1.3 Write写入方式:永久保存在磁盘中
        1.4 SQLite:采用SQLite数据库来存储数据。SQLite作为⼀一中小型数据库,应用ios中,跟前三种保存方式相比,相对比较复杂⼀一些

14.一个函数执行10次,有二次结果不正确 八次正确,你应 该怎么检查该bug。

1.首先既然有正确有错误,那么这个bug肯定是不一定会出错的,先看函数条件是否有漏写

  2.然后再检查函数是否会存在空的情况

    3.反复操作以上步骤去查明每个调用的函数结果都是正确的

15.Object-C有私有方法吗?私有变量呢

 ①objective-c – 类里面的方法只有两种, 静态方法和实例方法. 在Objective‐C中,所有实例变量默认都是私有的,所有实例方法默认都是公有的

  ②这似乎就不是完整的面向对象了,按照OO的原则就是一个对象只暴露有用的东西. 如果没有了私有方法的话, 对于一些小范围的代码重用就不那么顺手了. 在类里面是可以声名一个私有方法的,即延展。

    ③即OC中有私有变量:private修饰。OC中没有设计私有的方法。通过延展在实现文件里面定义方法作为私有的方法。但是这不是真正意义上的私有方法。(注意类目和延展的区别)

16.堆和栈的区别?
objective-c 对象所占内存总是分配在“堆空间”,并且堆内存是由你释放的,即release。
栈是由编译器管理自动释放的,在方法中(函数体)定义的变量通常在栈内。

1.栈区(stack):由编译器自动分配释放,存放函数的参数值,局部变量等值。其操作方式类似于数据结构中的栈。
  2.堆区(heap):一般由程序员分配释放,若程序员不释放,则可能会引起内存泄漏。注 堆和数据结构中的堆栈不一样,其类似于链表。

栈是一个用来存储局部和临时变量的存储空间。在现代操作系统中,一个线程会分配一个栈. 当一个函数被调用,一个stack frame(栈帧)就会被压到stack里。里面包含这个函数涉及的参数,局部变量,返回地址等相关信息。当函数返回后,这个栈帧就会被销毁。而这一切都是自动的,由系统帮我们进行分配与销毁。对于程序员来说,我们无须自己调度。

堆从本质上来说,程序中所有的一切都在内存中(有些东西是不在堆栈中的,但在这篇文章中我们不作讨论)。在堆上,我们可以任何时候分配内存空间以及释放销毁它。你必须显示的请求在堆上分配内存空间,如果你不使用垃圾回收机制,你必须显示的去释放它。这就是在你的函数调用前需要完成的事情。简单来说,就是malloc与free。

通常以这种方式创建对象:
NSObject *obj = [[NSObject alloc] init];
系统会在栈上存储obj这个指针变量,它所指的对象在堆中。通过        [NSObject alloc]系统会为其在堆中开辟一块内存空间,并为其生成    NSObject所需内存结构布局。

栈对象:
 优点:1.高速,在栈上分配内存是非常快的。
            2.简单,栈对象有自己的生命周期,你永远不可能发生内存泄露。因为他总是在超出他的作用域时被自动销毁了
 缺点:栈对象严格的定义了生命周期也是其主要的缺点,栈对象的生命周期不适于Objective-C的引用计数内存管理方法。

 在objective-c中只支持一个类型对象:blocks。关于在block中的对象的生命周期问题。出现这问题的原因是,block是新的对象,当你使用block时候,如果你想对其保持引用,你需要对其进行copy操作,(从栈上copy到堆中,并返回一个指向他的指针),而不是对其进行retain操作
  堆对象:
  优点:可以自己控制对象的生命周期。
  缺点:需要程序员手动释放,容易造成内存泄漏。

17.dispatch_barrier_async的作用是什么?

在并行队列中,为了保持某些任务的顺序,需要等待一些任务完成后才能继续进行,使用 barrier 来等待之前任务完成,避免数据竞争等问题。

dispatch_barrier_async 函数会等待追加到Concurrent Dispatch Queue并行队列中的操作全部执行完之后,然后再执行 dispatch_barrier_async 函数追加的处理,等 dispatch_barrier_async 追加的处理执行结束之后,Concurrent Dispatch Queue才恢复之前的动作继续执行。

打个比方:比如你们公司周末跟团旅游,高速休息站上,司机说:大家都去上厕所,速战速决,上完厕所就上高速。超大的公共厕所,大家同时去,程序猿很快就结束了,但程序媛就可能会慢一些,即使你第一个回来,司机也不会出发,司机要等待所有人都回来后,才能出发。 dispatch_barrier_async 函数追加的内容就如同 “上完厕所就上高速”这个动作。

(注意:使用 dispatch_barrier_async ,该函数只能搭配自定义并行队列 dispatch_queue_t 使用。不能使用: dispatch_get_global_queue ,否则 dispatch_barrier_async 的作用会和 dispatch_async 的作用一模一样。 )

上一篇下一篇

猜你喜欢

热点阅读