美文网首页
2019 面试题总结(附答案)

2019 面试题总结(附答案)

作者: iOS坚持者 | 来源:发表于2019-06-21 01:49 被阅读0次
  1. 如何令自定义的对象居右拷贝功能?
    对象需要实现 NSCopying 协议,实现 NSCpoying 协议中的copyWithZone方法
- (id)copyWithZone:(NSZone *)zone{    
     PersonModel *model = [[[self class] allocWithZone:zone] init];
     model.firstName = self.firstName;
     model.lastName  = self.lastName;
     //未公开的成员
     model->_nickName = _nickName;
     return model;
}

- (id)copyWithZone:(NSZone *)zone方法中,一定要通过[self class]方法返回的对象调用allocWithZone:方法。因为指针可能实际指向的是PersonModel的子类。这种情况下,通过调用[self class],就可以返回正确的类的类型对象。

  1. 说说你理解的 weak 属性?
    weak 是在 arc 机制下定义属性的修饰关键字,它修饰的属性不会强引用对象,当这个对象释放了,那么这个属性指针指向的地址也就为 nil。weak 修饰的属性会被放进一个 hash 链表里面,每一个节点的key 为这个对象的地址运用 hash 函数,节点的 value 是一个数组,将这个对象插入到数组中;释放的时候也是根据对象地址的 hash 函数值来将节点清空。

  2. Swift mutating 关键字作用?
    struct 和 enum 中用 mutating 修饰的方法可以修改属性。

  3. UIView 和 CALayer 层级关系?
    UIView继承 UIRepsond,能响应事件,
    CALayer 继承 NSObject,不能响应事件,只能用来渲染

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

@implementation Son : Father
-(id)init{
      if(self = [super init]){
        NSLog(@"%@",[self class]);
        NSLog(@"%@",[super class]);
      }
      return self;
}

都会输出:Son
通过查阅 objc 源码可得,

+ (Class)class {
    return self;
}

- (Class)class {
    return object_getClass(self);
}
Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}

[self class] 最后得到的依然是实例对象的类对象,即 Son
而 [super class] 表示的是去调用父类的class 方法,而根据多态特性,super 和 self 指向的是同一个接受者。

  1. @synthesize 和 @dynamic 有什么区别?
    @synthesize 为属性添加一个成员变量别名。同时会为该属性自动生成 setter/getter 方法。
    @dynamic 如果某属性已经在某处实现了自己的 setter/getter ,可以使用 @dynamic来防止 @synthesize 自动生成新的 setter/getter 覆盖;声明手动实现 get 和 set 方法。
    假如一个属性被声明为 @dynamic var,然后你没有提供 @setter方法和 @getter 方法。编译的时候没问题,但是当程序运行到 instance.var = someVar,由于缺 setter 方法会导致程序崩溃;
  2. iOS 动态特性
  • 动态加载
    按需求加载所需要的资源。
  • 动态类型
    id 类型,运行时才能根据语境识别类型
  • 动态绑定
    在运行时动态的添加方法,添加属性。
  1. 分类(category)、扩展(extension)、继承的区别?
  • 分类:在不改变原有代码基础上给类增加新的方法和属性。
  • 扩展:一般把不需要暴露的属性都定义在类扩展中。
  • 继承:面向对象语言的特性之一,子类可以使用父类的方法和属性。
  1. 为什么代理理要用weak?代理的delegate和dataSource有什么区别?block和代理理 的区别?
    用 weak 修饰代理是为了防止循环引用。
    delegate 偏重于交互,dataSource 偏重于数据的回调。
    block 是一个结构体,delegate 更加适合用来设计接口。
    详情:https://www.jianshu.com/p/ccfdabd0abef
    代理是一个设计模式,作为客户端和目标对象之间的中介。
    block 操作简单,容易造成内存泄露,delegate写法复杂,但是不用花太担心内存泄露问题。
    block 注重结果,而 delegate 注重过程。
  2. id和NSObject*的区别
    id 是动态类型,运行时才知道类型,NSObject *是静态类型,编译时就能调用方法
  3. 使⽤用系统的某些block api(如UIView的block版本写动画时),是否也考虑引⽤用 循环问题?
    不用担心,因为系统 block 不是 uiview 持有的
  4. 用@property声明的NSString(或NSArray,NSDictionary)经常使⽤用copy关键 字,为什么?如果改⽤用strong关键字,可能造成什么问题?
    copy 是浅拷贝,可变对象copy 一次得到是不可变对象,只要实现了协议方法。一般用来修饰不可变类型,用 strong 也可以,但是用 copy 更符合规范。
  5. static 有什么作用?
    修饰局部变量:只初始化一次,延长局部变量的生命周期,程序销毁变量才销毁。
    修改全局变量的作用范围只在当前文件下,定义在.h 文件中则作用域包括引入这个文件的类;而默认情况下全局变量作用域是在整个项目下的,同一个工程声明两个同名的全局变量会造成编译失败。
    用 static 通过改变全局变量的作用域,可以避免重复定义全局变量。
    详情:static extern const
  6. main()执行之前的过程有哪些?
    读取镜像文件,查找动态库,加载资源文件,注册类和分类。
  7. KVO 基本原理?
    KVO 注册监听的类会生成一个派生类,并把这个对象指针指向这个派生类,当改变属性值时,会调用属性的 set 方法,这时会通知调用监听方法。
  8. swift 模式匹配?
  • 通配符模式(Wildcard Pattern)
    如果你在 Swift 编码中使⽤用了了 _ 通配符,就可以表示你使⽤用了了通配符模式
    例如,下面这段代码在闭区间 1...10 中迭代,每次迭代都忽略该区间的当前值:
for _ in 0...10 {
    print("hello") 
}
  • 标识符模式(Identifier Pattern)
    定义变量或者常量时候,可以认为变量名和常量名就是一个标识符模式,⽤用于接收 和匹配⼀一个特定类型的值:
let i = 1 // i 就是⼀一个标识符模式
  • 值绑定模式(Value-Binding Pattern)
    值绑定在 if 语句和 switch 语句中用的较多。
    比如 if let 和 case let, 还有可能是 case var。 let 和 var 分别对应绑定为常量和 变量。
var str: String? = "nil"
if let v = str {
// 使⽤用v这个常量做什么事 (use v to do something)
print("hello")
}
  • 元组模式(Tuple Pattern)
    顾名思义,元组模式就是匹配元组中元素的模式:
let tuple = ("world", 21)
switch tuple {
case ("hello", let num):
  print("第⼀个元素匹配,第二个元素可以直接使用\(num)")
case (_, 0...10):
  print("元组的第⼀个元素可以是任何的字符串,第二个元素在0~10之间")
case ("world", _):
  print("只要元组的第⼀个元素匹配, num可以是任何数")
default: 
  print("default")
}

但是在 for-in 语句中,由于每次循环都需要明确匹配到具体的值,所以下代码 是错误的:

let pts = [(0, 0), (0, 1), (0, 2), (1, 2)]
for (x, 0) in pts {
}

需要使⽤用一个另外一个值绑定模式,来达成以上的逻辑:

let pts = [(0, 0), (0, 1), (0, 2), (1, 2)]
for (x, y) in pts {
}
  • 枚举⽤例模式(Enumeration Case Pattern)
    枚举⽤例模式是功能最强⼤的⼀个模式,也是整个模式匹配当中,应⽤⾯最广的模 式,结合枚举中的关联值语法,可以做很多事情。先看一个简单的例子:
enum Direction {
case up case down case left case right
}
let direction = Direction.up switch direction {
case .up:
  print("up") 
case .down:
  print("down") 
case .left:
  print("left") 
case .right:
  print("right")
}
  • 可选模式
    语句 case let x = y 模式允许你检查 y 是否能匹配 x。
    而 if case let x = y { ... } 严格等同于 switch y { case let x: ... }:当你只想与一条 case 匹配时,这种更紧凑的语法尤其有。有多个 case 时更适合使用switch。
    可选模式就是包含可选变量定义模式,在 if case、 for case、 switch-case 会⽤用 到。注意 if case let 和 if let的区别:
    使⽤可选模式匹配
 let a:Int? = nil
if case let x? = a {
  print("\(x)") }
let arr:[Int?] = [nil, 1, nil, 3]
/// 只匹配非nil的元素
for case let element? in arr {
  print("\(element)")
}
  • 类型转换模式(Type-Casting Pattern)
    使⽤ is 和 as 关键字的模式,就叫类型转换模式:
let tp : Any = 0
switch tp {
case let a as Int :
  print("是Int类型") 
case let a as String :
  print("是String类型") 
default:
  print("其它类型") 
}

switch tp { 
case is Int:
  print("是Int类型") 
case is String:
  print("是String类型") 
default:
  print("其它类型") 
}
  • 表达式模式
    表达式模式只出现在 switch-case 中,Swift的模式匹配是基于~=操作符的,如果 表达式的~= 值返回true则匹配成功。可以自定义~=运算符,自定义类型的表达式匹配 行为:
    ⾃定义模式匹配: 注意必需要写两个参数, 不然会报错
struct Affine {
var a: Int
var b: Int }
func ~= (lhs: Affine, rhs: Int) -> Bool { return rhs % lhs.a != lhs.b
}
switch 5 {
case Affine(a: 2, b: 0): 
  print("Even number") 
case Affine(a: 3, b: 1):
   print("3x+1")
case Affine(a: 3, b: 2): 
  print("3x+2") 
default:
   print("Other")
}
  1. objc在向一个对象发送消息时,发⽣生了了什么?
    先去obj对应的类中找缓存列表,找不到时去找方法列表;再找父类,以此向上传递;最后找不到则走消息转发流程。
#park 1. 第一次找不到时,会自动调用该方法,用来给程序动态添加一个新方法并执行
+(bool)resolveInstanceMethod:(sel){

}
#park 2. 当系统调用上一个方法后未能实现添加新方法,这是系统提供一个将SEL转给备用接受者的机会
-(id)forwardingTargetForselector:(sel)aselctor{

}
#park 3. 方法加签
-(NSMethodSigature *)methodSignatrueForseletor:(sel)aselector{

//当上一个方法返回nil或self时,进入该方法,返回一个方法签名,并由forwardInvocation:去执行
}
-(void)forwardInvocation:(NSInvocation *)anInvocation{

//若上面方法不返回nil,则在这里调用自己对象的其他方法,也可以调用其它函数,以及多个不同对象的多个方法
}
  1. 静态库的原理理是什什么?你有没有⾃自⼰己写过静态编译库,遇到了了哪些问题?

  2. runloop 有什么作用?runloop 和弦城有什么关系?
    runloop:字面意思就是跑圈,其实就是一个循环,用来处理线程里面的事件和消息。
    runloop 和线程之间的关系:每个线程如果不想被释放,就必须有一个 runloop 来不停的跑圈,以来处理线程里面的各个事件和消息。
    主线程默认开启一个 runloop,也就是这个线程保证程序正常运行,子线程默认是没有开启 runloop 的。
    runloop 有5种模式(mode),
  • tracking 模式,界面跟踪模式,用于追踪 scrollview触摸滑动,保证界面滑动时不受其他 mode 影响。
  • default 模式:app 默认的 mode,通常主线程就是在这个线程里面。
  • common 模式,这只是一个占位的 mode,没有实际作用。
  • initialization模式:app 启动时进入到第一个模式,启动完成之后就不再使用。
  • eventreceive模式:接收系统时间得 mode。
  1. 不手动指定 autoreleasepool 情况下,一个autorelease 对象什么时候释放?
    1.手动调用 autoreleasepool 去释放
    2.autorelease 对象在出了作用域之后会被自动添加到最近创建的自动释放池中,并会在当前 runloop 迭代结束时释放。而他能够释放的原因是同在每个 runloop 迭代中都加入了自动释放池 push 和 pop;
  1. OC 完整消息转发机制
  • resolveInstanceMethod:动态方法解析
  • forwardingTargetForSelector:备用接受者
  • forwardInvocation:方法加签
  1. scheduledTimer方式触发的 timer,在滑动页面列表是,timer 回暂停,为什么?怎么解决?
    timer创建会自动添加到 mode 为 default 模式的 runloop,列表在滑动时 runloop 模式会切换为 tracking,所以不会响应。解决办法就是把 timer 添加到模式为 common 的 runloop。
  2. 如何手动触发一个 value 的 KVO?
    KVO 原理是当为对象添加观察者时会重新生成一个派生类,并且把这个对象的 isa 指针指向这个派生类。
    手动触发 KVO,willChangeValueForKey和 didChangeValueForKey.
  3. 如何定位和分析项目中影响性能的地方?以及如何进行性能优化?
    instruments -> timer profile,检测应用耗时操作
    instruments -> leaks,检测内存泄露问题。
    instruments -> Core Animation,检测离屏渲染
    性能优化:
  • 耗时操作尽量放在子线程中
  • 控件颜色尽量不透明
  • imageview 大小和实际尺寸大小一只
  • 利用 tableview 重用机制
  • 懒加载
  • 尽量少用离屏渲染,离屏渲染是开辟一个新的帧缓冲区用于 GPU 渲染纹理,开辟这个缓冲区需要消耗性能,而且当这帧画面需要显示到屏幕上,也是需要环境上下文的切换这也是要付出代价的。
    以下操作都会引起离屏渲染:
    a。图层遮罩(layer.mask)
    b。为图层设置圆角和阴影
    c。使用CGContext在drawRect :方法中绘制大部分情况下会导致离屏渲染,甚至仅仅是一个空的实现
    d。文本(任何种类,包括UILabel,CATextLayer,Core Text等)
  • tableview 缓存高度
  • 创建全局nsdateformat对象
  1. 线程是什么?进程是什么?二者有什么区别?
    线程是任务的执行单元,进程负责调度任务的执行。每个进程至少有一个线程,进程占用内存空间,多个线程可以访问进程的资源。

  2. RunLoop 是什么?
    RunLoop 是一种事件循环机制,在有事件需要处理的时候则唤醒处理,没有消息需要处理则休眠以节约资源。
    线程和runloop 是一一对应的,主线程之外的线程在创建时并没有加入到 runloop 中,如果不主动添加到当前 runloop并运行,线程执行完就会销毁;所以,NSTimer 在主线程中会执行,在子线程中默认是不会执行的。在当前 runloop 只要是有 timer 和 source 消息需要处理就不会退出,一旦没有事件需要处理,runloop 就回退出。RunLoop 的创建是发生在第一次获取时,RunLoop 的销毁是发生在线程结束时。
    runloop 是一一对应的,线程会被添加到全局的 hash 表中。
    RunLoop能正常运行的条件就是,至少要包含一个Mode(RunLoop默认就包含DefaultMode),并且该Mode下需要有至少一个的事件源(Timer/Source),且运行在有事件源的Mode下。
    经过NSRunLoop封装后,只可以往mode中添加两类事件源:NSPort(对应的是source1)和NSTimer。

  3. NSURLSession与NSURLConnection区别
    https://guiyongdong.github.io/2016/11/18/NSURLSession%E4%B8%8ENSURLConnection%E5%8C%BA%E5%88%AB/

相关文章

网友评论

      本文标题:2019 面试题总结(附答案)

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