当我们谈论iOS时,我们都在讨论什么?

1. 什么是arc?(arc是为了解决什么问题诞生的?)

  • 什么是arc:
    arc就是自动引用技术,作用是编译器代替程序员为对象添加retain/release。(我觉得了解到这种程度就差不多了!)
  • arc是为了解决什么问题诞生的?
    对象的生命周期包括诞生和死亡。alloc一个对象obj,obj的retain+1,结束使用后调用[obj release],这样对象的retainCount为0,不会造成内存泄漏。MRC——>ARC的过程就是手动添加改为了自动添加的过程。
  • 那么为什么弃用MRC改用ARC呢?是为了解决retain和release不匹配错误,而导致不匹配的原因有:
  1. 创建一个对象,要确保该对象使用结束后销毁,避免造成内存泄露。(用完未销毁)
  2. 解决重复释放.(谁创建谁释放,谁释放,重复释放会导致奔溃)

2.请解释以下keywords的区别: assign vs weak, __block vs __weak

  • assign和weak都是用于修饰弱引用,而他们的区别是:
    assign:简单赋值,修饰基本数据类型(NSInteger)、c数据类型(int)和delegate(避免循环引用)。
  • 那么assign为什么不用来修饰对象呢?
    因为assign会导致下面这个问题:
    指针a指向一块内存,这时把a赋给了b,如果使用assign属性,a和b就指向同一块内存。如果这时候把a释放了,那么b在使用使用这块内存的时候就会crash.

weak:弱引用,修饰对象类型。

  • 为什么基本类型和C数据类型的修饰用assign?
    因为基本数据类型不是对象,在内存中创建和使用后,在一个方法体结束后就被删除。基本数据类型不需要引用计数,而其他修饰词需要引用计数,所以使用assign。

2.__block和__weak的区别:

  • __block:引用修饰,所以被__block修饰的变量的值是动态变化的,也就是可以被重新赋值。
    注意:如果在block内部要修改外部变量而不用__block修饰,会直接报错.
  • __weak:主要作用是避免循环引用, __weak typeof (self)weakSelf = self;

链接:iOS: ARC和非ARC下使用Block属性的问题

3. __block在arc和非arc下含义一样吗?

MRC下的__block相当于ARC下的__weak,而ARC下的__block是为了修饰一个变量能在block中被修改。

  • ARC循环引用解决:
    __weak:弱引用(首选方案)不会产生强引用,指向的对象销毁时,会自动让指针置为nil
    __unsafe_unretain:弱引用,不安全 ,指向的对象销毁时,指针存储的地址值不变
    __block:缺点就是必须执行block,清空指针
  • MRC循环引用解决:
    不支持__weak的
    __unsafe_unretain
    __block

4. 使用atomic一定是线程安全的吗?

  • atomic的作用是为属性的setter/getter方法加锁,避免多线程情况下访问setter/getter产生数据错乱。所以在访问setter/getter方法时是线程安全的,而对于进行其他操作的多线程则和atomic没有直接关系
  • 给属性加上atomic修饰,可以保证属性的setter和getter都是原子性操作,也就是说保证setter和getter内部都是线程同步
    它并不能保证使用属性的过程是线程安全的
    atomic在ios里基本上不适用,太消耗内存,一般在Mac上用

5. 描述一个你遇到过的retain cycle例子

  • 在-(void)dealloc里invalidate一个NSTimer对象。
这么做会retain cycle的原因:
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats

这个方法在官方文档里的说明:The timer maintains a strong reference to this object until it (the timer) is invalidated.解释:timer对象会对object拥有一个强引用,如果object是self,那么self就被timer retain,如果timer一直不invalidated,那么dealloc就一直不会被调用。

解决方法:在viewWillDisappear的时候invalidateNSTimer对象。

- (void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    [timer invalidate];
}
  • block中的retain cycle:
    block会对用到的对象做一个copy操作,retainCount+1,所以block对用到的对象就有了一个强引用。那么用到的对象是否对block有一个强引用是要看具体条件的。
下面就产生了循环引用
 [self doSomething:^(BOOL param) {
        self.tage = param;
    }];
    self.doSomethingWithBlock(YES);

6.+(void)load; +(void)initialize;有什么用处?

  • app启动后会自动调用每个类+ (void)load方法。
    先调用类的load方法,如果有继承了父类的,先调用load,然后在调用分类的load方法。
  • 当类第一次接受到消息时候调用initialze。
    某个类继承了父类的时候,先调用父类的initialze,在调用自己的initialze,注意,不需要调用super。如果之前已经调用过initialze就不会再initialze进行初始化。有分类的时候,有继承父类的,先调用父类的initialze,继承了父类的子类调用分类的initialze,因为在调用过程中分类的方法移到了子类的前面。

7. 为什么其他语言里叫函数调用, objective c里则是给对象发消息(或者谈下对runtime的理解)

当我们给对象发送消息时[tableView cellForRowAtIndexPath:indexPath],编译器会把这段Object-c代码转换为c的函数调用objc_msgSend(tableVIew,@selector(cellForRowAtIndexPath:),indexPath),其中indexPath是参数。

8. 什么是method swizzling?

黑魔法——偷梁换柱。每个类都有一个方法列表,存放着selector的名字和对应方法实现的映射关系。而IMP就是指向方法实现(类似指针)。method swizzling可以做到运行时更换selector指向对应方法实现的IMP。比如selectorA指向A实现,selectorB指向B实现,更换两个方法的IMP后,selectorA调用的就是B实现了。

9. UIView和CALayer是啥关系?

  • 1.在IOS上创建一个UIView就默认创建一个CALaye,可以通过如下方法获得CALayer *layer = self.view.layer;
  • 2.Layer提供内容绘制和动画,但它本身不能创建可视界面,需要添加到UIView.
  • 3.Layer不能响应事件。
  • CALayer和UIView的区别及联系:
    UIView是iOS系统中界面元素的基础,所有的元素界面都集成自它,UIView本身完全是由CoreAnimation来实现,真正的绘图部分是由一个CALayer类来管理,UIView更像是一个CALayer的管理类,所以访问它的与绘图和坐标相关的属性,如frame,bounds等,实际上都是在访问其所包含的CALayer的相关属性,因此,,可以在所有UIview的子类上实现动画效果。
    UIview是继承自UIResponder,能接受并相应事件,负责显示内容的管理,而CALayer继承自NSObject,不能响应事件,负责显示内容的绘制。
  • UIView可以响应事件,Layer不可以
    UIKit使用UIResponder作为响应对象,来响应系统传递过来的事件并进行处理。UIApplication、UIViewController、UIView、和所有从UIView派生出来的UIKit类(包括UIWindow)都直接或间接地继承自UIResponder类。
    在 UIResponder中定义了处理各种事件和事件传递的接口, 而 CALayer直接继承 NSObject,并没有相应的处理事件的接口。
  • UIView主要是对显示内容的管理而 CALayer 主要侧重显示内容的绘制
    UIView主要是对显示内容的管理, 而CALayer主要是显示内容的绘制. UIView是CALayer的CALayerDelegate, 在代理方法内部[UIView(CALayerDelegate) drawLayer:inContext]调用UIView的drawRect方法, 从而绘制出UIView的内容. UIView的显示内容由内部的CALayer:display方法来实现.编程问题都可以抽离出机制和策略部分。机制一旦实现,就会很少更改,但策略会经常得到优化。CALayer也可以看做是一种机制,提供图层绘制,CALayer的头文件基本上是没怎么变过的,而UIView可以看做是策略,变动很多。越是底层越是机制,越是机制就越是稳定。机制与策略分离,可以使得需要修改的代码更少,特别是底层代码,这样可以提高系统的稳定性。UIView遮蔽了大部分的CALayer接口,抽取构造出更易用的frame和动画实现,这样上手更容易。
  • frame, position, bounds调用
    一个CALayer的frame是由其anchorPoint, position, bounds, transform共同决定的, 而一个UIView的的frame只是简单地返回CALayer的frame, 同样UIView的center和bounds也只是简单返回CALayer的Position和Bounds对应属性
  • 在做 iOS 动画的时候,修改非 RootLayer的属性(譬如位置、背景色等)会默认产生隐式动画,而修改UIView则不会
    对于每一个 UIView 都有一个 layer,把这个 layer 且称作RootLayer,而不是 View 的根 Layer的叫做 非 RootLayer。我们对UIView的属性修改时时不会产生默认动画,而对单独 layer属性直接修改会,这个默认动画的时间缺省值是0.25s.
    在 Core Animation 编程指南的 “How to Animate Layer-Backed Views” 中,对为什么会这样做出了一个解释:(UIView 默认情况下禁止了 layer 动画,但是在 animation block 中又重新启用了它们)是因为任何可动画的 layer 属性改变时,layer 都会寻找并运行合适的 'action' 来实行这个改变。在 Core Animation 的专业术语中就把这样的动画统称为动作 (action,或者 CAAction)。
  • 为什么CALayer不能直接使用UIColor,UIImage
    首先,CALayer是定义在QuartzCore框架中的,CGImageRef、CGColorRef两种数据类型是定义在CoreGraphics框架中的
    ,而UIColor和UIImage是定义在UIKit框架中的。
    其次,QuartzCore框架和CoreGraphics框架是可以跨平台使用的,在iOS和Mac OS X上都能使用但是UIKit只能在iOS中使用。
    所以,为了保证可移植性,QuartzCore不能使用UIImage、UIColor,只能使用CGImageRef、CGColorRef。

10. 如何高性能的给UIImageView加个圆角?(不准说layer.cornerRadius!)

使用CoreGraphics将图片添加到graphics context中,绘制圆角路径然后对图片内容进行裁剪:

- (UIImage *)roundCornersOfImage:(UIImage *)image {
    CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
    
    UIGraphicsBeginImageContextWithOptions(image.size, false, 0);
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGPathRef path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:20].CGPath;
    CGContextAddPath(ctx, path);//将创建的路径添加到当前上下文路径中
    CGContextClip(ctx);//裁剪路径
    [image drawInRect:rect];
    UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return finalImage;
}

注意:比使用layer.cornerRadius性能高的原因在于对大量图片处理时(比如tableViewCell中的图片)。layer.cornerRadius需要的离屏渲染会占用额外的资源导致掉帧。而使用CoreGraphics不会引起这个问题。(CoreGraphics是耗时操作,最好异步调用)。ps. IOS9后直接cornerRadius设置图片的圆角不会引起离屏渲染问题,但是如果设置了背景等一些属性后再使用cornerRadius,还是会有屏渲染问题的。

11. 使用drawRect有什么影响?

  • 可以在这个方法里完成绘制视图的操作。这个方法执行时表明系统已经准备好绘制环境,用户可以进行绘制操作了。除了绘制,其他更改视图的行为最好不要在这个方法里完成,比如修改视图属性的行为,如背景色或者对view.layer的设置。
  • 视图在生命周期内触发这个方法的情况有多种,比如添加进视图层级或者重绘等。因此如果一个调用drawRect的视图在一个页面大量使用,可能会有性能问题。(我猜的,如果只是做了视图重绘,我认为一般是不会出问题的)

13. 麻烦你设计个简单的图片内存缓存器(移除策略是一定要说的)(SDWebImage源码学习)

同时使用内存缓存和硬盘缓存实现,其中内存缓存使用NSCache类。

  1. 存储:先将image对象直接存到cache,然后将image转为data写入到硬盘
  2. 读取:每次读取图片缓存,先去cache中查找。如果cache中不存在,则去disk中查找图片,然后把结果返回,同时保存到cache中。
  3. 图片移除:
  • 通知注册监听app进程移除和app进入后台两个事件。如果两个事件发生时,1.自动删除存放超过1周的图片;2.如果当前图片容量合计超过总容量,则按时间顺序删除旧的文件,直到容量达标。
  • 通知注册监听内存警告事件,当时间发生时,自动清空cache。

14. 讲讲你用Instrument优化动画性能的经历吧(别问我什么是Instrument)

开发中需要优化最多的就是tableView的滑动动画了。core Animation工具可以查看页面的fps。当滑动视图时,fps明显低于60则可能出现性能问题需要优化了。

Debug -> View Debugging -> Rendering之前是在core Animation里,现在单独拿出来了。

现在开发中会严重影响动画FPS的可能原因:

  1. 离屏渲染问题
    图片圆角、阴影等操作会引起离屏渲染,会产生严重掉帧问题。Color offscreen-Rendering Yellow可以查看是哪些视图产生了离屏渲染。
  2. 主线程阻塞
    比如使用coreGraphics绘图这种耗时操作,如果放在主线程里去实现就很可能引起tableView滑动卡顿。
  3. 优化:
  • cell中减少阴影圆角的使用,必须的话可以使用光栅化缓存,或者coreGraphics绘制
  • 耗时操作异步完成
  • 尽量别空含有透明度的控件
  • 图片尽量使用36bit格式,不要含有alpha通道,大小和视图frame匹配

15.loadView是干嘛用的?

  • 当跳转到一个vc时要用到self.view,而self.view==nil时,系统会调用loadView创建一个view赋值给self.view。
  • 我们可以重写loadView方法给self.view设置自定义view。这和 我们在viewDidLoad方法里创建视图添加到self.view的区别就是,通过loadView可以直接修改vc的rootView,即self.view的类型,前者的视图层级有两层,后者是一层。
  • loadView中初始化的view不需要设置frame,系统默认它等于[UIScreen mainScreen].bounds。

16. viewWillLayoutSubView你总是知道的。。

  • 和UIView的layoutSubviews类似。用于布局添加的子视图,只是平时我们都在viewDidLoad完成了。
  • 当vc.view的bounds变化时,系统会执行这个方法。比如你在vc旋转时,你可以在这个方法里重新给子视图布局,让他满足横向的界面。layoutSubviews和viewWillLayoutSubView都适合用于重新布局子视图的需求,不要在里面进行初始化操作,毕竟在一个周期里可能会执行多次,引起歧义.

17. GCD里面有哪几种Queue?你自己建立过串行queue吗?背后的线程模型是什么样的?

  1. 我觉得算4种,主队列、全局队列、串行队列、并行队列。
  2. 背后的线程模型是什么意思?我是在不懂,按我自己的理解说:
  • 任何队列使用dispatch_sync方式执行,不会创建新的线程,都是在当前线程里执行的。
  • 使用dispatch_async执行主队列中任务,那么不会创建线程,在主线程中执行
  • 使用dispatch_async执行全局队列或者并行队列,那么根据任务数开辟线程数
  • 使用dispatch_async执行串行队列,会开辟一个线程,串行执行

18.http的post和get啥区别?(区别挺多的,麻烦多说点)

  • get:
    获取服务器上的数据
    是通过URL传递数据,效率高
    请求的数据在URL上 不安全
    get请求的结果能够被浏览器缓存
  • post:
    一般是往服务器提交数据,并获取服务器返回的结果
    通过请求体传输数据 ,效率低
    请求的数据用户看不到,在请求提上 相对安全
    请求不能被浏览器缓存

19.UDP和TCP有什么区别:

  • 连接方面区别
    TCP面向连接(如打电话要先拨号建立连接)。
    UDP是无连接的,即发送数据之前不需要建立连接。
  • 安全方面的区别
    TCP提供可靠的服务,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达。
    UDP尽最大努力交付,即不保证可靠交付。
  • 传输效率的区别
    TCP传输效率相对较低。
    UDP传输效率高,适用于对高速传输和实时性有较高的通信或广播通信。
  • 连接对象数量的区别
    TCP连接只能是点到点、一对一的
    UDP支持一对一,一对多,多对一和多对多的交互通信。

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

runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会 dealloc,假如 weak 指向的对象内存地址是a,那么就会以a为键, 在这个 weak 表中搜索,找到所有以a为键的 weak 对象,从而设置为 nil。

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

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

22.如何用GCD同步若干个异步调用?(如根据若干个url异步加载多张图片,然后在都下载完成后合成一张整图)

使用Dispatch Group追加block到Global Group Queue,这些block如果全部执行完毕,就会执行Main Dispatch Queue中的结束处理的block。

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{ /*加载图片1 */ });
dispatch_group_async(group, queue, ^{ /*加载图片2 */ });
dispatch_group_async(group, queue, ^{ /*加载图片3 */ }); 
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 合并图片
});

23.dispatch_barrier_async的作用是什么?

  • 在并行队列中,为了保持某些任务的顺序,需要等待一些任务完成后才能继续进行,使用 barrier 来等待之前任务完成,避免数据竞争等问题。 dispatch_barrier_async 函数会等待追加到Concurrent Dispatch Queue并行队列中的操作全部执行完之后,然后再执行 dispatch_barrier_async 函数追加的处理,等 dispatch_barrier_async 追加的处理执行结束之后,Concurrent Dispatch Queue才恢复之前的动作继续执行。
  • 打个比方:比如你们公司周末跟团旅游,高速休息站上,司机说:大家都去上厕所,速战速决,上完厕所就上高速。超大的公共厕所,大家同时去,程序猿很快就结束了,但程序媛就可能会慢一些,即使你第一个回来,司机也不会出发,司机要等待所有人都回来后,才能出发。 dispatch_barrier_async 函数追加的内容就如同 “上完厕所就上高速”这个动作。

24.苹果为什么要废弃dispatch_get_current_queue?

dispatch_get_current_queue容易造成死锁

24.NSTimer有什么需注意的以及和RunLoop的关系?

  • 不管是重复性的timer还是一次性的timer都会对它的方法的接收者进行retain,这两种timer的区别在于“一次性的timer在完成调用以后会自动将自己invalidate,而重复的timer则将永生,直到你显式的invalidate它为止”。
  • timer不是一种实时的机制,会存在延迟,而且延迟的程度跟当前线程的执行情况有关。
  • 必须得把timer添加到runloop中,它才会生效。
  • 要让timer生效,必须保证该线程的runloop已启动,而且其运行的runloopmode也要匹配。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容