1、NSThread/NSOperation/GCD 三种多线程不同,分别使用场景?
•NSThread:
–优点:NSThread 比其他两个轻量级,使用简单
–缺点:需要自己管理线程的生命周期、线程同步、加锁、睡眠以及唤醒等。线程同步对数据的加锁会有一定的系统开销
•NSOperation:
–不需要关心线程管理,数据同步的事情,可以把精力放在自己需要执行的操作上
–NSOperation是面向对象的
•GCD:
–Grand Central Dispatch是由苹果开发的一个多核编程的解决方案。iOS4.0+才能使用,是替代NSThread, NSOperation的高效和强大的技术
–GCD是基于C语言的
2、响应链
iOS系统检测到手指触摸(Touch)操作时会将其打包成一个UIEvent对象,并放入当前活动Application的事件队列,单例的UIApplication会从事件队列中取出触摸事件并传递给单例的UIWindow来处理,UIWindow对象首先会使用hitTest:withEvent:方法寻找此次Touch操作初始点所在的视图(View),即需要将触摸事件传递给其处理的视图,这个过程称之为hit-test view。
UIWindow实例对象会首先在它的内容视图上调用hitTest:withEvent:,此方法会在其视图层级结构中的每个视图上调用pointInside:withEvent:(该方法用来判断点击事件发生的位置是否处于当前视图范围内,以确定用户是不是点击了当前视图),如果pointInside:withEvent:返回YES,则继续逐级调用,直到找到touch操作发生的位置,这个视图也就是要找的hit-test view。
hitTest:withEvent:方法的处理流程如下:
首先调用当前视图的pointInside:withEvent:方法判断触摸点是否在当前视图内;
若返回NO,则hitTest:withEvent:返回nil;
若返回YES,则向当前视图的所有子视图(subviews)发送hitTest:withEvent:消息,所有子视图的遍历顺序是从最顶层视图一直到到最底层视图,即从subviews数组的末尾向前遍历,直到有子视图返回非空对象或者全部子视图遍历完毕;
若第一次有子视图返回非空对象,则hitTest:withEvent:方法返回此对象,处理结束;
如所有子视图都返回非,则hitTest:withEvent:方法返回自身(self)。
3、__block与__weak的区别?
(1).__block不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型。
__weak只能在ARC模式下使用,也只能修饰对象(NSString),不能修饰基本数据类型(int)。
(2).__block对象可以在block中被重新赋值,__weak不可以。
(3).__weak本身是可以避免循环引用的问题的,但是其会导致外部对象释放了之后,block内部也访问不到这个对象的问题,我们可以通过在block内部声明一个__strong的变量来指向weakObj,使外部对象既能在block内部保持住,又能避免循环引用的问题.
__block本身无法避免循环引用的问题,但是我们可以通过在block内部手动把blockObj赋值为nil的方式来避免循环引用的问题。另外一点就是__block修饰的变量在block内外都是唯一的,要注意这个特性可能带来的隐患。
__block的理解是其实际上是把变量的作用域给改变了,应该是提升了变量的作用域,使得在block内部和外部所访问的是同一个变量。
4、SDImage框架的缓存机制
(1)UIImageView+WebCache:setImageWithURL:placeholderImage:options:先显示placeholderImage,同时由SDWebImageManager根据URL来在本地查找图片。
(2)SDWebImageManager:downloadWithURL:delegate:options:userInfo:SDWebIma
geManager是将UIImageView+WebCache同SDImageCache链接起来的类,SDImageCache:queryDiskCacheForKey:delegate:userInfo:用来从缓存根据CacheKey查找图片是否已经在缓存中
(3)如果内存中已经有图片缓存,SDWebImageManager会回调SDImageCacheDelegate :imageCache:didFindImage:forKey:userInfo:
(4)而UIImageView+WebCache则回调SDWebImageManagerDelegate:webImageManager:didFinishWithImage:来显示图片。
(5)如果内存中没有图片缓存,那么生成NSInvocationOperation添加到队列,从硬盘查找图片是否已被下载缓存。
(6)根据URLKey在硬盘缓存目录下尝试读取图片文件。这一步是在NSOperation进行的操作,所以回主线程进行结果回调notifyDelegate:。
(7)如果上一操作从硬盘读取到了图片,将图片添加到内存缓存中(如果空闲内存过小,会先清空内存缓存)。SDImageCacheDelegate回调imageCache:didFindImage:forKey:userInfo:。进而回调展示图片。
(8)如果从硬盘缓存目录读取不到图片,说明所有缓存都不存在该图片,需要下载图片,回调imageCache:didNotFindImageForKey:userInfo:。
(9)共享或重新生成一个下载器SDWebImageDownloader开始下载图片。
(10)图片下载由NSURLConnection来做,实现相关delegate来判断图片下载中、下载完成和下载失败。
(11)connection:didReceiveData:中利用ImageIO做了按图片下载进度加载效果。
(12)connectionDidFinishLoading:数据下载完成后交给SDWebImageDecoder做图片解码处理。
(13)图片解码处理在一个NSOperationQueue完成,不会拖慢主线程UI。如果有需要对下载的图片进行二次处理,最好也在这里完成,效率会好很多。
(14)在主线程notifyDelegateOnMainThreadWithInfo:宣告解码完成,imageDecoder:didFinishDecodingImage:userInfo:回调给SDWebImageDownloader。
(15)imageDownloader:didFinishWithImage:回调给SDWebImageManager告知图片下载完成。
(16)通知所有的downloadDelegates下载完成,回调给需要的地方展示图片。
(17)将图片保存到SDImageCache中,内存缓存和硬盘缓存同时保存。
(18)写文件到硬盘在单独NSInvocationOperation中完成,避免拖慢主线程。
(19)如果是在iOS上运行,SDImageCache在初始化的时候会注册notification到UIApplicationDidReceiveMemoryWarningNotification以及UIApplicationWillTerminateNotification,在内存警告的时候清理内存图片缓存,应用结束的时候清理过期图片。
(20)SDWebImagePrefetcher可以预先下载图片,方便后续使用。、
5、UITaBleView出现卡顿的可能情况
(1)imageView尽量设置为不透明
opque尽量设置为YES。
当imageView的opque设置为YES的时候其alpha的属性就会无效,imageView的半透明取决于其图片半透明或者imageView本身的背景色合成的图层view是半透明的。
如果图片全部不是半透明就不会触发图层的blend操作,整个图层就会不透明。
如果叠加的图片有出现半透明的,就会立马触发图层的blend操作,整个图层不透明。
opque设为NO。
当opque为NO的时候,图层的半透明取决于图片和其本身合成的图层为结果。
背景色尽可能设为alpha值为1,当某一块图层的alpha和其superView的背景色alpha不一样的时候会触发alpha合成操作,这是一项看似很简单但却是非常消耗CPU性能的操作。
(2)UIView的背景色设置
UIView的背景色尽量不要设置为clearColor,这样也会触发alpha叠加,在tableView滑动的时候是非常消耗性能的。子视图的背景色尽可能设置成其superView的背景色,这样图层合成的时候不会触发blend操作。最好不使用带alpha通道的图片,如果有alpha尽量让美工取消alpha通道。
(3)cell上layer尽量避免使用圆角
在工作中关于滑动界面我们会时常遇到cell行设置头像为圆角等需求,这时候我们尽量避免使用layder.cornerRadius,因为这会触发离屏渲染。离屏渲染很耗时间。
离屏渲染:是GPU渲染区的一个渲染缓冲区,我们所用的所有显示屏的图形图像都是通过GPU进行渲染,然后显示在屏幕上。GPU负责渲染会把渲染的图形放到缓冲区然后CPU就会发一个垂直信号显示到屏幕。
如果要使用圆角,我们可以设置为layer.shouldRasterize
= YES,其实这个设置是触发光栅化,可以大大提高渲染的性能。我的理解光栅化就是类似于cell的重用机制。
光栅化:把第一次渲染好的图层放到缓冲区,那么下次不需要再离屏渲染直接就可以从缓冲区拿去使用。
(4)优化图片的加载方式
我们都知道图片的加载方式有两种形式:
1、UIImage *headerImage = [UIImage
imageNamed:@"haodf.png";
2、UIImage * headerImage = [UIImageimageWithContentOfFile:@"haodf.png"];
我们讲讲两种加载图片方式的区别:
第一种:当我们经常需要这张图片并且仅仅是小图的时候,我们可以使用此种方式加载图片。
这种方式是把图片缓存在图片缓存区,当我们使用的时候会通过图片的名字也就是通过key的方式去查找图片在缓存区的内存地址。
当我们使用很多图片的时候系统就会开辟很多内存来存储图片,所以qq、微信我们很多时候都会去清除缓存操作。
第二种:当我们使用工程里面的一张大图并且使用次数很少甚至为1次的时候,我们优先会采用这种方式加载图片,这种方式当使用完图片的时候会立即丢弃释放资源,所以对性能不会带来负担。
(5)尽量延迟图片的加载
当我们在滑动页面的时候尤其对于那种布局特别复杂的cell,滑动的时候不要加载图片,当滑动停止得时候再进行图片的加载。
我们都知道不管是UITableView还是ScrollView在滚动的时候需要显示东西都是通过runLoop去拿。
当滚动的时候runLoop会处于NSRunLoopTrackingMode的模式,我们可以通过一个主线程队列dispatch_after或者selfPerformSelector设置runLoop的模式为NSDefaultRunLoopMode模式,就可以做到停止滚动再加载图片。
注:其实严格意义上selfPerformSelector的事件就是在主线程队列中等待。
(6)优先加载理念
一直很好奇墨迹天气这款app基本都是很炫的图片,是如何做到滑动时候不卡顿的呢,在cocoachina上有幸认识了一位墨迹天气的大牛,说是采用优先加载的理念,既先展示一部分,当滑动的时候再加载下面的一部分这样就保持流畅。至于具体没透露。
(7)避免阻塞主线程
让图片的绘制、图片的下载、对象的创建、文本的渲染等这些耗时的操作尽可能采用子线程异步的方式去处理,对于layer及UI的操作不得不在主线程里面,只能想办法优化,所以此处给大家推荐Facebook的得力之作ASDK,有兴趣的可以好好研究下,现在好多公司都进行采用。
(8)xib、storyBoard、纯代码
苹果推出storyboard确实为开发者节省了大量的时间,提高了开发效率,但是对于那种
复杂的滑动界面,利用storyboard是非常消耗资源的,不信的可以试试用性能工具timeProfie看看cpu所占的性能百分比,其CPU的资源远远大于纯代码布局,我看了一个国外的网站介绍,这两种方式初始化对象分配内存的先后方式完全不一样,至于具体细节有兴趣的可以找相关资料研究。
当然对于那种重用性不强固定不怎么变化的界面还是很喜欢storyboard,一蹴而就,节省成本。
6、imageName和imageWithContentsOfFile、init的区别
第一种方法(imageName)为常见方法,利用它可以方便加载资源图片。用imageNamed的方式加载时,会把图像数据根据它的名字缓存在系统内存中,以提高imageNamed方法获得相同图片的image对象的性能。即使生成的对象被autoReleasePool释放了,这份缓存也不释放。而且没有明确的释放方法。如果图像比较大,或者图像比较多,用这种方式会消耗很大的内存。
第二种方法(imageWithContentsOfFile)加载的图片是不会缓存的。得到的对象时autoRelease的,当autoReleasePool释放时才释放。
第三种方法(init)要手动release掉。不系统缓存。release后立即释放,一般用在封面等图比较大的地方。
用imageNamed的方式加载时,系统会把图像Cache到内存。如果图像比较大,或者图像比较多,用这种方式会消耗很大的内存,而且释放图像的内存是一件相对来说比较麻烦的事情。例如:如果利用imageNamed的方式加载图像到一个动态数组NSMutableArray,然后将将数组赋予一个UIView的对象的animationImages进行逐帧动画,那么这将会很有可能造成内存泄露。并且释放图像所占据的内存也不会那么简单。但是利用imageNamed加载图像也有自己的优势。对于同一个图像系统只会把它Cache到内存一次,这对于图像的重复利用是非常有优势的。例如:你需要在一个TableView里重复加载同样一个图标,那么用imageNamed加载图像,系统会把那个图标Cache到内存,在Table里每次利用那个图像的时候,只会把图片指针指向同一块内存。这种情况使用imageNamed加载图像就会变得非常有效。
7、nil、Nil、NULL与NSNUll的区别
(1)nil
指向一个对象的指针为空 ,在Objective-C中用于id类型的对象
NSString
*name = nil;
(2)Nil
指向一个类的指针为空,在Objective-C中用于Class类型的对象
Class aClass = Nil;
(3)NULL
指向C类型的指针为空,多用于如下例子:
int*pInt = NULL;
(4)NSNull
在Objective-C中是一个类,只是名字中有个Null,NSNull有+
(NSNull *)null;单例方法,多用于集合(NSArray,NSDictionary)中值为空的对象
8、bool与BOOL的区别
有的文章对bool与BOOL的区别做以下解释,但这种解释是错误的,BOOL b = 8960;结果是YES.
objective-c中的BOOL实际上是一种对带符号的字符类型(signed char)的类型定义(typedef),它使用8位的存储空间。通过#define指令把YES定义为1,NO定义为0。
注意:objective-c并不会将BOOL作为仅能保存YES或NO值的真正布尔类型来处理。编译器仍将BOOL认作8位二进制数,YES和NO值只是在习惯上的一种理解。
问题:如果不小心将一个大于1字节的整型值(比如short或int)赋给一个BOOL变量,那么只有低位字节会用作BOOL值。如果该低位字节刚好为0(比如8960,写成十六进制为0x2300),BOOL值将会被认作是0,即NO值。而对于bool类型,只有true和false的区别,即0为false,非0为true。
举例:
BOOL b1=8960; //实际是NO,因为8960换成十六进制为0x2300,BOOL只有8位存储空间,取0x2300的低8位,00,所以是NO
bool b2=8960;//实际是true,因为bool类型,非0即为true。
9、int与NSInteger的区别
当需要使用int类型的变量的时候,可以像写C的程序一样,用int,也可以用NSInteger,但更推荐使用NSInteger,因为这样就不用考虑设备是32位的还是64位的。
楼主你可以看如下定义:
#if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
typedef long NSInteger;
typedef unsigned long NSUInteger;
#else
typedef int NSInteger;
typedef unsigned int NSUInteger;
#endif
32位编译器:
char:1个字节
char*(即指针变量): 4个字节(32位的寻址空间是2^32,即32个bit,也就是4个字节。同理64位编译器)
short int : 2个字节
int:4个字节
unsigned int : 4个字节
float: 4个字节
double: 8个字节
long: 4个字节
long long: 8个字节
unsigned long: 4个字节
64位编译器:
char:1个字节
char*(即指针变量): 8个字节
short int : 2个字节
int:4个字节
unsignedint : 4个字节
float:4个字节
double:8个字节
long:8个字节
long long:8个字节
unsigned long:8个字节
10、IOS系统架构
iOS的系统架构分为四个层次:核心操作系统层(Core OS layer)、核心服务层(Core
Services layer)、媒体层(Media layer)和可触摸层(Cocoa Touch layer)。
1、Core OS是位于iOS系统架构最下面的一层是核心操作系统层,它包括内存管理、文件系统、电源管理以及一些其他的操作系统任务。它可以直接和硬件设备进行交互。作为app开发者不需要与这一层打交道。
2、Core Services是核心服务层,可以通过它来访问iOS的一些服务。
3、Media是媒体层,通过它我们可以在应用程序中使用各种媒体文件,进行音频与视频的录制,图形的绘制,以及制作基础的动画效果。
4、Cocoa Touch是可触摸层,这一层为我们的应用程序开发提供了各种有用的框架,并且大部分与用户界面有关,本质上来说它负责用户在iOS设备上的触摸交互操作。
11、CoreFoundtion对Socket的操作方法
Core Foundation层:因为直接使用socket需要更多的编程工作,所以苹果对OS层的socket进行简单的封装以简化编程任务。该层提供了CFNetwork和CFNetServices,其中CFNetwork又是基于CFStream和CFSocket。
12、Object-c的类可以多重继承么?可以实现多个接口么?Category是什么?重写一个类的方式用继承好还是分类好?为什么?
Object- c的类不可以多重继承;可以实现多个接口,通过实现多个接口可以完成C++的多重继承;Category是类别,一般情况用分类好,用Category去重写类的方法,仅对本Category有效,不会影响到其他类与原有类的关系。
13.自动释放池的作用
自动释放池:是用来自动释放对象的,不需要关心对象释放的时间,不需要关心对象什么时候调用release
自动释放池释放的时间:事件循环结束
自动释放池原理: 当池子结束,会想池子里面的所有对象发送一条release消息
自动释放池使用: 1.创建对象2.加入自动释放池
14、AFN 2.0与AFN 3.0的不同?
AFNetworking 2.0开始使用NSURLConnection的基础API,以及较新基于NSURLSession的API的选项。AFNetworking 3.0现已完全基于NSURLSession的API,这降低了维护的负担,同时支持苹果增强关于NSURLSession提供的任何额外功能。
由于Xcode 7中,NSURLConnection的API已经正式被苹果弃用。虽然该API将继续运行,但将没有新功能将被添加,并且苹果已经通知所有基于网络的功能,以充分使NSURLSession向前发展。
15、Git集中式还是分布式?Git与SVN代码管理方式的区别?
Git分布式管理
(1)GIT是分布式的,SVN不是:
这是GIT和其它非分布式的版本控制系统,例如SVN,CVS等,最核心的区别。
GIT跟SVN一样有自己的集中式版本库或服务器。但,GIT更倾向于被使用于分布式模式,也就是每个开发人员从中心版本库/服务器上chect out代码后会在自己的机器上克隆一个自己的版本库。可以这样说,如果你被困在一个不能连接网络的地方时,就像在飞机上,地下室,电梯里等,你仍然能够提交文件,查看历史版本记录,创建项目分支,等。对一些人来说,这好像没多大用处,但当你突然遇到没有网络的环境时,这个将解决你的大麻烦。
(2)GIT把内容按元数据方式存储,而SVN是按文件:
所有的资源控制系统都是把文件的元信息隐藏在一个类似.svn,.cvs等的文件夹里。如果你把.git目录的体积大小跟.svn比较,你会发现它们差距很大。因为.git目录是处于你的机器上的一个克隆版的版本库,它拥有中心版本库上所有的东西,例如标签,分支,版本记录等。
(3)GIT分支和SVN的分支不同:
分支在SVN中一点不特别,就是版本库中的另外的一个目录。如果你想知道是否合并了一个分支,你需要手工运行像这样的命令svn propget svn:mergeinfo,来确认代码是否被合并。感谢Ben同学指出这个特征。所以,经常会发生有些分支被遗漏的情况。
然而,处理GIT的分支却是相当的简单和有趣。你可以从同一个工作目录下快速的在几个分支间切换。你很容易发现未被合并的分支,你能简单而快捷的合并这些文件。