魏名华

不要偷懒,做更好的自己

Nothing


No Welcome Message

面试题收集

1、property 常用的后面修饰符,你回答的越多越好,但是,他会一个个问用法,然后再问你为啥,有啥特殊情况,怎么处理

2、MLeakFinder 的源码看了没,怎么实现的?你能怎么去用到你的代码中?

3、OC、JS 交互,具体的实现方式,JS 中的一个按钮点击后,webView都进行了哪些操作?从这个按钮中你能获取到哪些信息,能够做哪些处理?

4、UILabel、UIButton、UITableView 跟 NSObject 啥关系?具体讲讲

5、A 里有B、C 两个view,B中有两个按钮E、F,当我点击F按钮,编译器进行了哪些操作?

6、AOP解释下,可以用到哪些地方,有啥优缺点?

1: weak,assign的区别? 指针变量用weak修饰,基本数据类型和结构体需要用assgin。 因为assign是指针赋值,不对引用计数操作,使用之后如果没有置为nil,可能就会产生野指针;而weak一旦不进行使用后,会自动赋值为nil,不会产生野指针!

2: strong,retain,copy的区别? strong相当于retain,二者都是指针拷贝。当有其他对象引用当前对象时,会拷贝一份当前对象的地址,这样它就也指向当前对象了。所以,还是同一个对象,只是retainCount+1;对应的setter方法,是将_property先release(_property release),然后将参数retain(property retain),最后是_property = property。

copy:对于不可变对象copy采用的是浅复制,引用计数器加1(其实这是编译器进行了优化,既然原来的对象不可变,复制之后的对象也不可变那么就没有必要再重新创建一个对象了);对于可变对象copy采用的是深复制,引用计数器不变(原来的对象是可变,现在要产生一个不可变的当然得重新产生一个对象);对应的setter方法,是将_property先release(_property release),然后拷贝参数内容(property copy),创建一块新的内存地址,最后_property = property

3: 除了以上说过的还有哪些属性? atomic,nonatomic,readonly,readwrite,getter和setter

4: 谈谈封装,你是如何封装的? 提取共性,我会将项目中会经常用到的方法封装在一个global的公共类中,对于每个界面或者controller用到的都会封装在baseController或者baseView内。

5: app开发中最擅长的是那个方面的技术? UI方面的技术(此题会引出下题,以后面试要谨慎咯,虽然我确实对这块挺擅长的,但是很多都是懂原理没操作过,然后又打脸了,因为没写完😭)

6: 手写一个基于scrollview的tableview(20分钟左右)?(这题我原理懂了,可惜没写完,因为从没写过,虽然最后说了想法,不过没写完也是完蛋了) (待上传)

7: tableview的优化方案? —对cell进行重用 —将所有cell内的透明视图添加背景色 —创建一个NSDictionary,用来存放高度计算结果,避免高度重复计算 —将所有圆角,半角这些能用背景图实现的全部不要用代码完成 —滑动时按需加载,这个在大量图片展示,异步网络加载!(但基本各个都用sd来异步了,不知道是否必要,也写上吧) —尽量少用addView给Cell动态添加View,可以初始化时就添加,然后通过hide来控制是否显示 —减少subviews的数量 —将cell中耗时的绘图工作异步

8: 一份刚接到手的代码如何快速定位出错的位置,如果渲染时很卡很慢要如何解决? 1.看报错信息进行定位 2.没报错信息时直接进入该界面打断点调试 3.如果没入界面,那就直接看手机的错误日志进行定位

1.如果是切换界面的时候卡,那么设置个不透明的背景色即可 2.如果是tableview滑动时卡,那么把大部分的绘制图像,下载等耗时操作全部异步(见优化方案) 3.如果多选图片后展示卡,不可以用imageName来设置,必须保存沙盒用本地相片路径来设置图片

9: app如何进行测试? 用instruments(具体怎么用,请看http://www.cnblogs.com/luoxiaofu/p/5314376.html)

10: 开发生涯中遇到最难的问题,怎么解决的? (这题是最不好答的一题,答的简单显得你技术太低,答的难一深究就露馅,博主也答不好,哎,此题自由发挥吧)

1.先来个自我介绍—>介绍完后 你是用什么语言开发的,OC?Swift?还是C++? (OC) 2.那你说下OC是一门怎样的语言 (巴拉巴拉哔哔一通) 3.那你刚才说他底层是消息机制,那么什么是消息机制?(回答有说到objc_msgSend) 4.那你说下如果对一个空对象发送消息,是如何流转的?(巴拉巴拉… ) 5.那什么叫运行时机制 (…) 6.说下strong,weak,assign。(哔里巴拉…说weak时,提到了block) 7.block的存储域问题(栈区比如if、for的大括号,临时性的,用完就会释放;堆区通过copy…又一顿哔哔) 8.为什么要放到堆区 (保持引用,避免用到时,在内存中找不到…) 9.说下MVC (又一顿哔哔…) 10.那你数据请求在哪里做 (C,然后又说也可以放到M里做<感觉自己萌萌哒,已经被虐傻了,极度不自信>;提到了MVVM) 11.那你怎么看MVVM(我的理解是不论是MVVM,MVCS,MVP都是从MVC中演化来的;MVVM中的MV和MVC的MVC作用相同,VM是用来做数据相关的操作的,比如网络请求…哔哩吧啦又哔哔了一顿。这时感觉这位面试官大牛已经露出失望的神色了~~) 12.一个页面,只有一个tableview的这种简单页面,要同时保证数据的正确获取,和UI界面的正确刷新(当时一问我就蒙了,想了下,应该是数据安全的问题,该加锁,我就说应该加锁,保证数据安全。然后大牛就说怎么加锁,加什么锁。 好吧,我承认我多嘴了,我就不该回答,因为我根本不知道加什么锁,我就说加互斥锁,好吧又来了,那什么叫互斥锁,我说不是很清楚…,那你说下有几种锁吧,我说不清楚…好了感觉要凉了~ 走的时候,请教了大牛这个问题。大牛说再yy直播里面,主播开播,关播,cell都会增加减少。这时,用户点击这个cell<实际已经不存在了的>,这时就会造成crash) 13.你刚经常提到selector,它是具体是什么(完全懵逼,之前还记得来着。当时已经傻掉了,只能说不会,不记得了) 好了,今天就先面到这,后续如果有复试,我们再联系你,你的基础比较薄弱,请~

  1. SDWebImage原理 在iOS的图片加载框架中,SDWebImage使用频率非常高,很多公司项目都有用到,所以熟悉SDWebImage的原理是一个重要并且基础的功底。 有兴趣的可以看我的简书SDWebImage总结。
  2. iPad和iPhone如何适配 BOOL getRuntimeClassIsIpad() { if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) //iPhad设备 { return TRUE;
    } return FALSE; }

  3. UITableView性能优化 可以参考我的简书 UITableView性能优化技巧,里面详细介绍了一些tableView性能优化的技巧和注意事项。
  4. APP启动时间优化 可以参考我的简书iOS优化APP启动时间,里面详细介绍了一些常用的优化技巧及注意事项。
  5. 同时下载多张图片,都完成后再做事情,如何处理 可以参考我的简书iOS开发多线程-下载合并图片,一共介绍了四种方法。

1.为什么说Objective-C是一门动态语言 1.Objective-C是一门运行时语言。在编译阶段不确定所有的东西,在运行时,根据运行环境确定对象的类型,对象对应类的方法实现等。 2.它的动态主要体现在3个方面,动态类型、动态绑定、动态加载 3.动态类型如id(弱类型); 动态绑定(将方法调用的时机推迟到运行时,在编译阶段,方法的调用不和代码绑定。只有消息发送出来之后,才会确定调用的代码); 动态加载(如动态资源加载,@2x、@3x)

2.什么是消息机制 在Objective-C中,方法调用是一个消息发送的过程(在java,C++等静态语言中是函数调用)。 OC对象发送一个消息的过程如下: 1.向对象发送消息 2.在缓存中查找是否有匹配方法。如果有则响应,否则继续 3.在对象的类对象的方法列表中(method list)查找是否有匹配方法,如果有则响应,否则继续 4.在对象的父类的缓存中查找是否有匹配方法。如果有则响应,否则继续 5.在对象的父类的方法列表中(method list)查找是否有匹配方法。如果有则响应,否则继续 6.进入动态解析 -(BOOL)resolveInstanceMethod:(SEL)sel;- (BOOL)resolveClassMethod:(SEL)sel;查看是否有动态添加方法实现。如果有则响应,否则继续 7.进入消息重定向 -(id)forwardingTargetForSelector:(SEL)sel;如果有指定消息接收对象则响应,否则(指:返回nil或者self)继续 8.进入方法签名- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector返回方法签名并进入下一步,否则调用doesNotRecongnizeSelector:方法并抛出异常。 9.进入消息转发 - (void)forwardInvocation:(NSInvocation *)aInvocation;如果有指定消息转发对象则响应,否则调用doesNotRecongnizeSelector:方法并抛出异常。

3.说下copy、strong、weak、assign copy会重新开辟新的内存空间来保存一份相同的数据。被赋值对象(copy修饰的对象)和原对象值的修改互不影响。 strong和weak指针都指向原对象值的地址。原对象值修改,也随之修改。区别是前者会对原对象的引用计数+1,防止原对象被释放;后者不会,当原对象引用计数为0时被释放时,weak的值也为nil了。 weak和assign都是弱引用一个对象,不会对该对象的引用计数+1。但是当被引用对象的引用计数为0时,assign修饰的对象会发生野指针错误,原因是assign虽然不会对引用对象的引用计数+1,但是当引用对象值改变时,也不会销毁指向引用对象的指针。 而weak修饰的对象会随引用对象值改变而销毁指向引用对象的指针。 retain与strong相同 , weak,assign修饰的属性验证

4.block的使用及它的存储域问题 简单理解 block 是一个可以带有变量的匿名函数,实际上block也是一个OC对象。 block可能会存储在堆区(_NSConcreteMallocBlock)、全局数据区(_NSConcreteGlobalBlock)、栈区(_NSConcreteStackBlock)<栈上的block仅在MRC环境下存在>。 在ARC环境下,block声明既可以用copy也可以用strong。使用copy是在MRC时代遗留的习惯问题。因为在ARC时,编译器会自动将block从栈区拷贝到堆区。避免在使用时,已被释放。 weak属性经常配合block使用,避免循环引用问题。<注:weak修饰符在iOS 5引入,只能在ARC环境下使用> __block修饰基本数据类型。可以使其在block内部进行修改其值。原因从下图源码中可以看出,正常定义一个变量 a 时, block的实现中 是与外部变量 a 的定义结构相同;当使用__block修饰时,a会变为一个指针,使用指针引用外部变量 a 的地址。

最后附一篇介绍block及__block的文章 5.MVC设计模式 MVC是一种架构模式,M表示MOdel,V表示视图View,C表示控制器Controller: Model负责存储、定义、操作数据; View用来展示书给用户,和用户进行操作交互; Controller是Model和View的协调者,Controller把Model中的数据拿过来给View用。 Controller可以直接与Model和View进行通信,而View不能和Controller直接通信。 View与Controller通信需要利用代理协议的方式,当有数据更新时,MOdel也要与Controller进行通信,这个时候就要用Notification和KVO,这个方式就像一个广播一样,MOdel发信号, Controller设置监听接受信号,当有数据更新时就发信号给Controller,Model和View不能直接进行通信,这样会违背MVC设计模式。

6.在定义 property 的时候,atomic 和 nonatomic 有何区别? atomic 和 nonatomic 的区别在于,系统自动生成的getter/setter 方法不一样。如果你自己写 getter/setter,那 atomic/nonatomic/retain/assign/copy 这些关键字只起提示作用, 写不写都一样。 对于atomic的属性,系统生成的 getter/setter 会保证 get、set 操作的完整性,不受其他线程影响。 比如,线程 A 的 getter 方法运行到一半,线程 B 调用了 setter:那么线程 A 的 getter 还是能 得到一个完好无损的对象。 而nonatomic就没有这个保证了。所以,nonatomic的速度要比atomic快。 不过atomic可并不能保证线程安全。如果线程 A 调了 getter,与此同时线程 B 、线程 C 都调了 setter——那最后线程 A get 到的值,3种都有可能:可能是 B、C set 之前原始的值,也可能是 B set 的值,也可能是 C set 的值。同时,最终这个属性的值,可能是 B set 的值,也有可能是 C set 的 值。

转自链接:https://www.jianshu.com/p/7288eacbb1a2 简单验证如下:

  • (void)gcdGroup { /// init property name self.name = @”origin”; dispatch_group_t group = dispatch_group_create(); dispatch_queue_t queue = dispatch_queue_create(“com.bonlion.queue”, DISPATCH_QUEUE_CONCURRENT);

    /// thread A dispatch_group_async(group, queue, ^{ self.name = @”A”; NSLog(@”thread A->%@”, [NSThread currentThread]); }); /// thread B dispatch_group_async(group, queue, ^{ self.name = @”B”; NSLog(@”thread B->%@”, [NSThread currentThread]); }); /// thread C dispatch_group_async(group, queue, ^{ NSLog(@”name value is ->%@”, self.name); NSLog(@”thread C->%@”, [NSThread currentThread]); }); dispatch_group_notify(group, queue, ^{ NSLog(@”notify thread is used”); }); }

7.用@property声明的 NSString / NSArray / NSDictionary 经常使用 copy 关键字,为什么?如果改用strong关键字,可能造成什么问题? 答:用 @property 声明 NSString、NSArray、NSDictionary 经常使用 copy 关键字,是因为他们有 对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋 值操作(就是把可变的赋值给不可变的),为确保对象中的字符串值不会无意间变动,应该在设置新属性值 时拷贝一份。

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

总结:使用copy的目的是,防止把可变类型的对象赋值给不可变类型的对象时,可变类型对象的值发送变化 会无意间篡改不可变类型对象原来的值。

转自链接:https://www.jianshu.com/p/5595ae4262ed 8.系统对象的 copy 与 mutableCopy 方法 不管是集合类对象(NSArray、NSDictionary、NSSet … 之类的对象),还是非集合类对象 (NSString, NSNumber … 之类的对象),接收到copy和mutableCopy消息时,都遵循以下 准则:

  1. copy 返回的是不可变对象(immutableObject);如果用copy返回值调用mutable对象的 方法就会crash。
  2. mutableCopy 返回的是可变对象(mutableObject)。 【总结一句话】: 只有对不可变对象进行copy操作是指针复制(浅复制),其它情况都是内容复制(深复制)!

链接:https://www.jianshu.com/p/5595ae4262ed 9.KVC底层实现 当一个对象调用setValue:forKey: or valueForKey:方法时,方法内部会做以下操作: 1). 检查是否存在相应的 key 的setter or getter方法,如果存在,就调用setter or getter方法。 2). 如果setter or getter 方法不存在,就会查找与 key 相同名称并且带下划线的成员变量, 如果有,则直接给成员变量属性赋值 or 取值。 3). 如果没有找到_key,就会查找相同名称的属性key,如果有就直接赋值 or 取值。 4). 如果还没有找到,则调用 setValue:forUndefinedKey: 和valueForUndefinedKey: 方法。 以上两个方法的默认实现都是抛出异常,我们可以根据需要进行重写。

10.KVO的原理 附一份KVO简单实现——有详细注释 KVO深层次的讲解 facebook的自释放KVO 基本概念: KVO又称键(key)值(value)监听(observing)。 KVO的底层实现依赖于强大的runtime库。 KVO通过addObserver:forKeyPath:options:context:添加观察者对象(observer),观察目标对象 的某个属性(key)的新值或者旧值(options)的改变,关键字(context)可以作为区分使用 的方式实现值改变的回调机制。 使用场景: 1.一般使用于对scrollView的偏移量(contentOffset)值改变的监听。 2.商城购物车中改变商品数量,更新总价 底层原理: 在注册观察者之后,编译器会通过runtime的API动态创建注册一个中间类(派生类),继承自原本类(父类),然后将父类的 isa 指针指向这个子类(中间类)。同时,为子类动态添加了设置(setter)方法。 随后在 setter 方法中实现成对出现的 willChangeValueForKey: 与 didChangeValueForKey: 方法及父类的 setter 方法。然后再在didChangeValueForKey:方法中实现监听者的 observeValueForKeyPath: 方法,实现监听属性改变的过程。

大致过程如下:

  • (void)viewDidLoad { [super viewDidLoad];

    Person *p = [[Person alloc] init]; // 表明观察策略–> 观察新值 与 旧值的改变 NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld; // 注册观察者,监听者为当前控制器,监听对象为 p。 [p addObserver:self forKeyPath:@”age” options:options context:@”as you like”];

}

  • (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context { NSLog(@”\nkeyPath->%@\nchange->%@\ncontext->%@”, keyPath, change, context); }

断点之后,发现使用runtime获取到的p对象所属的类为 NSKVONotifying_Person。 中间类的setter方法实现大致如下 可以通过runtime得知,中间类的类名及其对应的方法列表的方法名。中间类只有setter方法,同时还有一个 class 方法,说明它重写了NSObject类的class方法。为什么这么做呢?因为它内部的实现大致这样- (Class)class{return object_getClass(class_getSuperclass(self));} 就可以做到过程欺骗。因为你使用[p class]方法得到的类还是Person类,除非使用runtime API 才能获得到真实类。 11.iOS是如何管理内存的 首先我们要知道,对于创建的普通对象,由于我们现在的项目都是ARC的,不需要我们过多干涉,而且记住内存管理的黄金法则就可以应付 而对于Block的内存管理来讲,很容易造成循环引用,因此在用到Block的地方一定要小心内存管理问题,最好在基类controller里重写dealloc,加一句打印日志,来查看类有没有得到释放,如果无打印信息,则说明这个类一只被引用而没有被释放,很有可能在用到block的地方发生循环引用了,对于block中需要引用外部cotroller的属性或者成员变量的时候,一定要使用弱引用。 如果不是block的问题再去查看是不是定时器或者代理引发的

#**Objective-C内存管理机制 ** Objective-C中的对象都是基于引用计数来管理生命周期的。简单来说就是,我们在需要持有一个对象时,调用retain让它的引用计数+1。不需要这个对象的时候,调用release让它的引用计数-1。当一个对象引用计数为0的时候,这个对象就会被自动销毁。 MRC 我们在手动管理引用计数的时候,要明确地控制对象的生命周期,显式的调用每一个retain和release。我们必须清楚的了解每个接口对引用计数的处理(如把一个对象放到数组里引用计数会被+1,用alloc创建的对象的引用计数一开始就是1,用哪些接口创建的对象是已经被调用过autorelease的等等)。在处理引用计数时稍有疏忽,就可能导致程序崩溃或内存泄漏。 ARC ARC是编译器通过对代码的静态分析,确定对象的生命周期,并在合适的位置自动加上retain和release的机制。把内存管理交给编译器以后,我们不需要再调用任何的retain和release了。ARC减少了MRC带来的思考负担,减少了内存问题出现的可能性,也大幅减少了代码量。

重点来了11.谈谈 ffmpeg ? 首先介绍以下..具体资料加我QQ972491872: 可以免费送大家

FFmpeg 简介 FFmpeg 是一个开源免费跨平台的视频和音频流方案,属于自由软件,采用 LGPL 或 GPL 许可证(依据你选择的组件)。它提供了录制、转换以及流化音视频的完整解决方 案。它包含了非常先进的音频/视频编解码库 libavcodec,为了保证高可移植性和编解 码质量,libavcodec 里很多 codec 都是从头开发的。 ffmpeg 项目由以下几部分组成: 1.ffmpeg 视频文件转换命令行工具,也支持经过实时电视卡抓取和编码成视频文 件. 2.ffserver 基于 HTTP、RTSP 用于实时广播的多媒体服务器.也支持时间平移 3.ffplay 用 SDL 和 FFmpeg 库开发的一个简单的媒体播放器 4.libavcodec 一个包含了所有 FFmpeg 音视频编解码器的库.为了保证最优性能和 高可复用性,大多数编解码器从头开发的. 5.libavformat 一个包含了所有的普通音视格式的解析器和产生器的库 2.1.2 FFmpeg 安装 1.将所有源代码压缩在一个文件夹中,例如/绝对路径/ffmpeg。 2.在终端输入以下指令: Cd /绝对路径/ffmpeg ./configure (此时,会出现问题。然后重新输入./configure –disable-yasm-) Make 至此,ffmpeg 安装编译通过,可以进行对音视频的操作。 ffplay 的编译需要依赖于 SDL 库,所以要想编译成功 ffplay,必须先安装 SDL 库, 安装方法:下载最新版本的 SDL 相应版本的 SDL 源码,编译,即可生成 SDL 库。

关于iOS消息转发 https://www.jianshu.com/p/ad3791a2add0

你使用过Objective-C的运行时编程(Runtime Programming)么?如果使用过,你用它做了什么?你还能记得你所使用的相关的头文件或者某些方法的名称吗?

你实现过多线程的Core Data么?NSPersistentStoreCoordinator,NSManagedObjectContext和NSManagedObject中的哪些需要在线程中创建或者传递?你是用什么样的策略来实现的?

Core开头的系列的内容。是否使用过CoreAnimation和CoreGraphics。UI框架和CA,CG框架的联系是什么?分别用CA和CG做过些什么动画或者图像上的内容。(有需要的话还可以涉及Quartz的一些内容)

是否使用过CoreText或者CoreImage等?如果使用过,请谈谈你使用CoreText或者CoreImage的体验。

NSNotification和KVO的区别和用法是什么?什么时候应该使用通知,什么时候应该使用KVO,它们的实现上有什么区别吗?如果用protocol和delegate(或者delegate的Array)来实现类似的功能可能吗?如果可能,会有什么潜在的问题?如果不能,为什么?(虽然protocol和delegate这种东西面试已经面烂了…)

你用过NSOperationQueue么?如果用过或者了解的话,你为什么要使用NSOperationQueue,实现了什么?请描述它和GCD的区别和类似的地方(提示:可以从两者的实现机制和适用范围来描述)。

既然提到GCD,那么问一下在使用GCD以及block时要注意些什么?它们两是一回事儿么?block在ARC中和传统的MRC中的行为和用法有没有什么区别,需要注意些什么?

您是否做过异步的网络处理和通讯方面的工作?如果有,能具体介绍一些实现策略么?

对于Objective-C,你认为它最大的优点和最大的不足是什么?对于不足之处,现在有没有可用的方法绕过这些不足来实现需求。如果可以的话,你有没有考虑或者实践过重新实现OC的一些功能,如果有,具体会如何做?

你实现过一个框架或者库以供别人使用么?如果有,请谈一谈构建框架或者库时候的经验;如果没有,请设想和设计框架的public的API,并指出大概需要如何做、需要注意一些什么方面,来使别人容易地使用你的框架。