基础面试题:
1. copy 和 strong 的区别。
也就是 深拷贝和浅拷贝 的区别。 copy属于深拷贝 其性质是开辟新的内存空间指向新的值,不同的内存地址互不干涉,使⽤ copy 的⽬的是为了让本对象的属性不受外界影响。strong属于浅拷贝 那么这个属性就有可能指向⼀个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性
常会被问到!!!
1. NSMutableArray为什么不可以用 copy 修饰?
其原因是:在调用self.数组 增删改 系统方法的时候会奔溃。 copy 修饰的 NSMutableArray的数组会被当成 NSArray 不可变数组执行, 当self 调用底层的 get/set 方法时候 没有响应的增删改方法。所以会奔溃。
经常使用 copy 关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操作!
2. NSString 为什么用copy 修饰?
使用copy修饰之后,即使属性拷贝来自可变字符串,也会被深拷贝成不可变字符串, 例如在字符串appendString 拼接或者是等等操作时。开辟的是另一块新的内存空间、修改前的值不会随着修改后的值而发生变化。所以为了避免可变字符串类型的值被修改用copy
2. 关于常见的block问题
1. block 为什么用copy修饰?
2. _ _ block 为什么可以修改block变量
3. 关于block 循环引用的问题
来自内心灵魂深处的三问!!!
block 为什么用copy修饰?
首先得理解 堆和栈 的关系! 堆是程序员 手动的分配和释放内存、栈是由 系统分配释放内存。
在MRC的时候 block所持有的对象内存是默认在栈区的、栈的特点就是创建对象随着可能被销毁、一旦销毁的对象再次调用就会奔溃。 用copy修饰后block会被放在堆区一般由程序员自己释放,他的生命 周期就是随着对象的销毁而结束。
ARC 下 使用 Strong、Copy 修饰 Block,都会将栈区的 Block 拷贝到堆区。
所有说block使用copy修饰 算是ARC下对于MAC的一个默认延续吧
_ _ block 为什么可以修改block变量 ?
使用了 __blcok 之后,在 block 被 copy 到堆上的同时也会将捕获的外部变量 copy 到堆上,之后便可以在 block 内部对外部变量进行修改
关于block 循环引用的问题 ?
一个对象中强引用了block,在block中又强引用了该对象,就会发射循环引用。
block中使用self,self.xxBlcok或者成员变量block 导致了循环引用 。 __weakSelf来代替self解决等。 __weak __tyof (self) weakselfi = self
这里常常会有一个延伸: 问的是AFNetWorking 中的Block使用怎么会不引起循环引用?
AF3.0 一下 AFURLConnectionOperation 里的一个请求结束之后,setCompleteBlock会把block设置为nil,来打破循环引用 .
所谓循环引用,是因为当前控制器在引用着block,而block又引用着self即当前控制器,这样就造成了循环引用。AFN中的block的调用并不在当前控制器中调用,那么这个self就不代表当前控制器,那自然也就没有循环引用的问题
3. ios13适配问题
1. 私有方法 KVC 可能导致崩溃。在 iOS 13 中部分方法属性不允许使用 valueForKey、setValue:forKey: 来获取或者设置私有属性,具体表现为在运行时会直接崩溃
2. 使用 presentViewController 方式打开视图,默认的如下图所示的视差效果,通过下滑返回。解决方法就是将 modalPresentationStyle 改回 Fullscreen 样式
3.MPMoviePlayerController 被弃用
4.在 iOS 13 中,苹果将原来蓝牙申请权限用的 NSBluetoothPeripheralUsageDescription 字段,替换为 NSBluetoothAlwaysUsageDescription 字段。
5. 第三方登录中心必须接入苹果登录
等等有一些导航栏 搜索框的改变 这上面我就不一一列举了。有心得同学可以自己去查阅一下文档
4. Runloop的简单理解
第一次获取时被创建、线程结束被销毁。 多线程时 主线程默认开启 子线程的手动开启
[NSRunLoop currentRunLoop]
概念: 运行循环机制。内部是一个do-while 不断处理各种任务(NSTimer、触摸事件、点击事件),节约CPU资源提高程序性能 ,底层是CFRunloop.
这边有一个延伸问题: UIScrollView 的滚动会导致 NSTimer 失效为什么?
在tableview滑动时timer就是显示暂停,原因是timer的这个简便构造方法把timer加入了NSRunLoopDefaultMode上,而tableview在滑动时只会处理UITrackingRunLoopMode,也就是说当前的RunLoop并没有功夫处理timer事件。 指定消息循环的模式为CommonModes(无论runloop运行在哪个mode,都能运行)
- kCFRunLoopDefaultMode, App的默认运行模式,通常主线程是在这个运行模式下运行
- UITrackingRunLoopMode, 跟踪用户交互事件(用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他Mode影响) -
kCFRunLoopCommonModes, 伪模式,不是一种真正的运行模式 - UIInitializationRunLoopMode:在刚启动App时第进入的第一个Mode,启动完成后就不再使用
- GSEventReceiveRunLoopMode:接受系统内部事件,通常用不到
注意iOS 对以上5中model进行了封装NSDefaultRunLoopMode;NSRunLoopCommonModes
为什么说提高了性能?
因为在没有事件发生的时候处于休眠状态,有事件发生的时候处于工作状态 。
5. SDWebimage 原理相关问题!!!
我这里只做一个简述。 只针对面试可能遇到的一些提问。具体问题你可以去查询一下源码或者找一些大神的博客看一看。
其作用: 就是图片的下载、缓冲、下载进度监控。
1. 缓冲图片的名称 MD5 为防止重名
2.内存警告是如何处理的 ? 利用通知中心观察 、clear Memory清理内存缓冲 clear Disk 清理磁盘缓冲。
3.缓冲时间是一周
4.clear和clean的区别?
clear 连同缓冲文件夹删除、再创建一个新的。clean 先删除过期的文件、再计算缓冲大小删除、按照创建时间删除知道maxsize
5.默认缓冲文件路径 : default下 6. 最大并发数6条 7.超时时间15S 8.缓冲机制:NSCache
SDWebimage的工作流程:
1. setImageWithURL 先设置请求网址和占位图片
2. 先从内存图片查找图片, 如果查到回调到 SDWebImaFFgeManage中显示图片
3. 如果有没查到就 生成opeartion 添加到队列查找磁盘、根据 URLKey从缓冲目录读取。读取到添加到内存缓冲中、再回调到manage类中展示
4. 如果没换没有查询到 就重新下载
5. 下载完的图片加入缓存中,并写入到磁盘中;
6.整个获取图片的过程是在子线程中进行,在主线程中显示。
6. weak如何实现自动赋nil?
runtime 对注册的类会进行布局,weak对象会被放到一个hash中。用weak指向的对象内存地址作为key。当此对象的引用计数为0的时候dealloc,在这个weak表中搜索找到所有以key为键weak的对象从而设置为nil
7.自动释放池问题
常用到是for循环创建大量变量的时候 !!!
每次调用每次都会创建一个新的对象。如果不是通过alloc、new、copy等创建的,那么他们的内部都会有一个autorelease进行自动释放,会等到循环结束进行释放而此时会消耗大量内存资源造成内存溢出。运行缓慢 。autorelease实际上只是把对release的调⽤延迟了,对于每⼀个autorelease,系统只是把该Object放⼊了当前的Autorelease pool中,当该pool被释放时,该pool中的所有Object会被调⽤Release。
@autoreleasepool 可以完美的解决
8. 涉及到地图持续定位耗电问题解决方案
原因是:持续的调用didupdatelocation方法
解决方案“:
1. 距离过滤器 distanceFilter 指定距离调用代理方法
2. 设置精确度 通过计算过程的降低达到省电
9. set/get 方法返回self属性奔溃
self. 实际上相当于 set/get 的调用。 导致循环调用 所以会奔溃
11. KVO原理
因为时间问题我就不贴代码了。只是将KVO建立观察者后 的一个流程梳理一下。
1、利用RuntimeAPI动态生成一个子类NSKVONotifying_XXX,并且让instance对象的isa指向这个全新的子类
2、当修改对象的属性时,会在子类NSKVONotifying_XXX调用Foundation的_NSSetXXXValueAndNotify函数
3、在_NSSetXXXValueAndNotify函数中依次调用
1) willChangeValueForKey 2)父类原来的setter 3)didChangeValueForKey,didChangeValueForKey:内部会触发监听器(Oberser)的监听方法( observeValueForKeyPath:ofObject:change:context:)
2、如何手动触发KVO方法
手动调用willChangeValueForKey和didChangeValueForKey方法
3、直接修改成员变量会触发KVO吗
不会触发KVO,因为KVO的本质就是监听对象有没有调用被监听属性对应的setter方法,直接修改成员变量,是在内存中修改的,不走set方法
4、不移除KVO监听,会发生什么
不移除会造成内存泄漏但是多次重复移除会崩溃。系统为了实现KVO,为NSObject添加了一个名为NSKeyValueObserverRegistration的Category,KVO的add和remove的实现都在里面。在移除的时候,系统会判断当前KVO的key是否已经被移除,如果已经被移除,则主动抛出一个NSException的异常
11. delegate 代理用weak 修饰?
weak修饰对象只指向对象,并不保持delegate对象,释放由外部控制
strong该对象强引用delegate对象,形成稳妥对象和被委托对象相互拥有,由于外部不能销毁而导致引起循环引用问题。
12. 关于图片的上传和压缩问题
AF中的 AFHTTPSessionManager就可以实现。主要涉及的就是如果是多张图上传50张、500张考虑一个占用内存过大而崩溃的问题,可以通过加到自动释放池解决。
压缩问题: 例如传的图片大、耗时 耗流量
可以通过 UIImageJPERepresontation方法。可以设置压缩指数。 例如加入判断 image>1M 压缩指数设置为0.7 0.5M<imahe<1M 调整成0.8 小于0.5M 写成0.9或者1 都可以
13.TableView优化
1.提前计算好cell的高度和布局(因为tableview回调的时候会多次的调用heightForRow的方法),获取数据的时候计算cell,保存到对应的model中,调用行高直接获取model 就行
2.数据量大的时候异步加载的方式进行加载 ,避免堵塞主线程
3.滑动时按需加载对应的内容,
14.性能优化
1. 页面布局: 对象的创建、销毁(广播、定时器等等),文本控件的计算排版等等
2.卡顿优化: 2.1 尽量提前计算好布局,一次性调整对应属性避免多次修改
2.2 使用多线程。如果线程开辟太多的话可以结合信号量(消耗CPU)
2.3 数据量大的时候考虑使用本地数据(尽量一次性写入,避免频繁写入数据)
3. 网络优化: 1. E网、4G、WiFi下设置不同的超时时间
2. 使用断点续传,网络不稳定的手可能会出现多次传输相同的内容到服务器
3. 使用缓冲减少网络请求
4. 耗电优化: 1.涉及到定位尽量不要使用实时请求,定位精度尽量不要太精确
2.涉及到蓝牙 、广播等等 设置一个合理的请求时间
6.内存优化: 1. 涉及到通知、定时器等等释放的问题
2. tableview. 行高了提前计算避免多次调用行高方法,数据大分页显示
3. 图片处理,尽量和UIImageView大小相同避免运行中缩放,多做缓冲
4. 大量的临时对象加入Autorelease Pool中执行,避免内存过高
5. 数据库的储存问题
15. 信号量
应用场景: 访问有限的资源、 或者是多线程中的一些特定请求
主要用到的函数: dispatch_semaphore_create创建信号量设置初始值
dispatch_semaphore_signal 发送信号,信号量+1
dispatch_semaphore_wait 等待信号 , 如果大于零则减掉1个信号量,往下执 行,如果等于零则阻塞该线程
1. 多个网络请求无序返回后再刷新界面?
创建线程组dispatch_group_create,最后再notify中刷新界面。结合信号量使用这样可 以保证在没有所有数据返回之前,notify里的内容一直不会执行
2. 多个网络请求有序返回输出?
1.信号量
2. 用NSOPerationQueue中的依赖关系 [ A addDependency:B ]
16. 关于折线图、柱形、饼状图
Chares 图标库(swift写的),需要创建一个桥接,oc-swift
或者是在UIView函数方法drawRect中自定义贝塞尔曲线 画柱形设置(x,y,w,h) 设置填充颜色等等
17. AFNetWorking 实现原理:
主要是用于对网络数据的请求和实时监控网络的状态,由五个部分组成:
1. AFURLSessionManage:核心类,负责请求的建立、管理和销毁等功能
2. AFURLRequest: 请求头的解码、序列化、优化处理、简化拼接过程
3. AFURLRepose: 用于网络返回数据的处理
4. AFNetWork : 监控网络请求的变化
5. UIKit:对于iOS UIKit的扩展库
这里会常常被问到: AFN网络请求的block内使用self不会造成循环引用?
答案是:AFNetworking是封装了一个completionBlock,AFURLConnectionOperation 里的一个请求结束之后,setCompleteBlock会把block设置为nil,来打破循环引用 .
18. 沙河目录分析:
分为三个目录: Documents、 Library( Caches、Preferences )、 tmp
Documents:保存应用运行需要持久化的数据, sqlite数据库
tmp: 保存运行时所需的临时数据
Library-->Preferences : NSUserDefaults
20. HTTPS 协议
1.向后台开发者获取SSL证书(crt格式),并将该文件的格式转换成cer格式
2. 双击该文件,在 keychain (钥匙串访问)中找到该文件的证书,项目然后导出cer文件。
3. AFN 3.0配置 先导入证书 证书由服务端生成,使用证书验证模式。 如果是需要验证自建证书,
4. info.plist,配置白名单
22. runtime面试题
OC的动态性就是由Runtime来支撑和实现的,Runtime是一套C语言的API,封装了很多动态性相关的函数
说说OC的消息机制?
1) OC中的方法调用其实都是转成了objc_msgSend函数的调用,给receiver(方法调用者)发送了一条消息(selector方法名)
2)objc_msgSend底层有3大阶段
2.1消息发送:
2.1.1.通过方法名字去 类对象 方法缓存中查找,如果有则返回方法地址
2.1.2.如果没有缓存,则会遍历 方法列表查找
2.1.3.查到了方法返回,并添加到 缓存列表
2.1.4.如果没找到则会去父类缓存中查找,在去父类方法列表中查找,一层一层父类往上找
2.2动态方法解析
如果是 对象方法调用会 调用_class_resoveInstanceMethod()如果是 类方法调用 调用 _class_resoveClassMethod()
2.3消息转发
-(id)forwardTargetForSelector:(SEL) aSelector
-(void)forwardInvocation:(NSInvocation *)anInvocation
runtime具体应用?
1)分类添加方法 2)字典转模型 3)KVC/KVO 4 ) 方法交换
method swizzling 方法交换:
23. load 和initalize 区别
load: 在main函数之前通过函数内存地址调用。程序启动时候加载所有的类、每个类只调用一次。不需要继承父类实现。
initalize: 在main函数之后通过objc_msgSend调用。
load和initialize方法都不用显示的调用父类的方法而是自动调用,即使子类没有initialize方法也会调用父类的方法,而load方法则不会调用父类。
24. 分类不能添加属性的原因
分类里添加属性,只是将该属性添加到该类的属性列表,并声明了setter和getter方法,但是没有生成相应的成员变量,也没有实现setter和getter方法。所以说分类不能添加属性 ,如果手动实现的话 改变内存的分布情况,这对编译性语言是灾难,是不允许的 。
分类只能扩展方法(属性仅仅是声明,并没真正实现)
25 事件的响应流程Fr
1.首先通过 hitTest:withEvent: 确定第一响应者,以及相应的响应链
2.判断第一响应者能否响应事件,如果第一响应者能进行响应则事件在响应链中的传递终止。如果第一响应者不能响应则将事件传递给 nextResponder也就是通常的superview进行事件响应
3.如果事件继续上报至UIWindow并且无法响应,它将会把事件继续上报给UIApplication
4.如果事件继续上报至UIApplication并且也无法响应,它将会将事件上报给其Delegate
5.如果最终事件依旧未被响应则会被系统抛弃
26 单元测试
介绍: 主要是xcode自带的XCTest. 简单应用场景就是(测试一些功能是否正常、代码的覆盖率等等,性能和逻辑的测试)例如要测试一个分享功能,避开重启APP进入分享界面、点击分享输入分享内容这一些列繁琐的操作。 测试很简单、主要是看自己架构设计
- [ setup ] 测试用例初始化方法 执行之前调用
- [ textXXX ] 要测试的实例方法,以text开头不含任何参数,测定预期值
- [ tearDown ] 清楚测试方法, 执行之后被调用
逻辑测试:testLogic 方法 里面 XCTAssertEqual 设定预期值验证
性能测试:testPrefromanceExample 方法 block里面做操作设置性能值选择代码通过率
异步测试:testAsync方法
UI测试: 这个是运行程序自动生成测试代码验证
27 KVC 简单分析
当一个对象调用setValue方法时,方法内部会做以下操作:
1). 检查是否存在相应的key的set方法,如果存在,就调用set方法。
2). 如果set方法不存在,就会查找与key相同名称并且带下划线的成员变量,如果有,则直接给成员变量属性赋值。
3). 如果没有找到_key,就会查找相同名称的属性key,如果有就直接赋值。
4). 如果还没有找到,则调用valueForUndefinedKey:和setValue:forUndefinedKey:方法。这些方法的默认实现都是抛出异常,我们可以根据需要重写它们。
28 KVO 简单分析
KVO-键值观察机制,原理如下:
1.当给A类添加KVO的时候,runtime动态的生成了一个子类NSKVONotifying_A,让A类的isa指针指向NSKVONotifying_A类,重写class方法,隐藏对象真实类信息
2.重写监听属性的setter方法,在setter方法内部调用了Foundation 的 _NSSetObjectValueAndNotify 函数
3._NSSetObjectValueAndNotify函数内部a) 首先会调用 willChangeValueForKeyb) 然后给属性赋值c) 最后调用 didChangeValueForKeyd) 最后调用 observer 的 observeValueForKeyPath 去告诉监听器属性值发生了改变 .
4.重写了dealloc做一些 KVO 内存释放
29 button防止多次点击
1. 设置enabled或userInteractionEnabled属性
2. 借助cancelPreviousPerformRequestsWithTarget:selector:object实现
// 此方法会在连续点击按钮时取消之前的点击事件,从而只执行最后一次点击事件+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(nullable id)anArgument;
// 多长时间后做某件事情- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;
3. 通过runtime交换方法实现
{
1 创建一个UIButton的分类,使用runtime增加public属性cs_eventInterval和private属性cs_eventInvalid。
2 在+load方法中使用runtime将UIButton的-sendAction:to:forEvent:方法与自定义的cs_sendAction:to:forEvent:方法进行交换
3 使用cs_eventInterval作为控制cs_eventInvalid的计时因子,用cs_eventInvalid控制UIButton的event事件是否有效。
}
30 WKWebview
WKWebview的白屏的问题:
原因:1. WKWebview是一个多进程组件,内存占用太大的时候会在加载中奔溃导致白屏。2. 网络问题。
解决:1.清理缓冲 重新加载 iOS 9以后 WKNavigtionDelegate 新增了一个回调函数。-(void)webiewXXXTermi nate方法在即将出现白屏的时候回调用这个方法。里面执行[webview reload]
WKWebView设置自定义UserAgent:
1. WKWebView的customUserAgent会覆盖webview本身的userAgent;2.configuration.applicationNameForUserAgent设置的userAgent是拼接在webview本身的userAgent后面的。
正确设置自定义userAgentWKWebViewConfiguration *configuration = [WKWebViewConfiguration new];configuration.applicationNameForUserAgent = "iOS";_webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration];
使用方法:WKWebView来加载网页,使用WKWebViewConfiguration来配置JS交互。
js调用OC
1. 主要使用WKUserContentController,用来做 原生与JavaScript的交互管理
2.使用协议类WKScriptMessageHandler,用来处理监听JavaScript方法从而调用原生OC方法。
3. 通过 接收JS传出消息的name 进行捕捉的回调方法
OC调用JS
使用WKUserScript,执行自定义的JavaScript代码
31 MVVM
MVVM就是在MVC的基础上分离出业务处理的逻辑到viewModel层,
即:model层,API请求的原始数据、数据持久化view层视图展示,由viewController来控制viewModel层,负责?络请求、业务处理和数据转化简单来说,就是API请求完数据,解析成model,之后在viewModel中转化成能够直接被视图层使?的数据,交付给前端。经过viewModel转化之后的数据由viewModel保存,与数据相关的处理都将在viewModel中处理。viewModel返回给view层view层是由viewController控制的。view层只做展示,不做业务处理。view层的数据由viewModel提供。
32 什么时候会报unrecognized selector的异常?
objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,如果,在最顶层的父类中依然找不到相应的方法时,会进入消息转发阶段,如果消息三次转发流程仍未实现,则程序在运行时会挂掉并抛出异常unrecognized selector sent to XXX 。
33 runtime如何通过selector找到对应的IMP地址?
每一个类对象中都一个方法列表,方法列表中记录着方法的名称,方法实现,以及参数类型,其实selector本质就是方法名称,通过这个方法名称就可以在方法列表中找到对应的方法实现.
34 objc在向一个对象发送消息时,发生了什么?
objc在向一个对象发送消息时,runtime会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,如果一直到根类还没找到,转向拦截调用,走消息转发机制,一旦找到 ,就去执行它的实现IMP 。
35 AutoreleasePool自动释放池
AutoreleasePool(自动释放池) 是OC中的一种内存自动回收机制,在释放池中的调用了autorelease方法的对象都会被压在该池的顶部(以栈的形式管理对象)。当自动释放池被销毁的时候,在该池中的对象会自动调用release方法来释放资源,销毁对象。以此来达到自动管理内存的目的
36 APP的启动
APP的冷启动可以概括为3大阶段:dyld、runtime、main
1. dylddyld(dynamic link editor),Apple的动态链接器,可以用来装载Mach-O文件(可执行文件、动态库等)。启动APP时,dyld所做的事情有:装载APP的可执行文件,同时会递归加载所有依赖的动态库.当dyld把可执行文件、动态库都装载完毕后,会通知Runtime进行下一步的处理.
2. runtime启动APP时,runtime所做的事情有:调用map_images进行可执行文件内容的解析和处理在load_images中调用call_load_methods,调用所有Class和Category的+load方法进行各种objc结构的初始化(注册Objc类 、初始化类对象等等)调用C++静态初始化器和attribute((constructor))修饰的函数
3. main接下来就是UIApplicationMain函数,AppDelegate的application:didFinishLaunchingWithOptions:方法
4. APP启动优化

37 @synthesize/@dynamic
@synthesize 表示如果属性没有手动实现setter和getter方法,编译器会自动加上这两个方法。@dynamic 告诉编译器:属性的 setter 与 getter 方法由用户自己实现,不自动生成。
38 埋点
Flurry: 埋点类——方法列表部署
39 GCD
GCD会自动管理线程的生命周期, 比如创建线程, 调度任务, 销毁线程等等操作.
同步执行: 在GCD里是sync, 不会开启新线程, 只会在当前线程进行操作.
异步执行: 在GCD里是async, 可以另外开启一个新的线程执行任务.
并行队列: 全名为Concurrent Dispatch Queue, 指的是可以让多个任务同时执行, 如果用到并行队列的话, 是会自动开启多个线程同时执行任务
.串行队列: 全名Serial Dispatch Queue, 指的是任务一个接一个的执行, 完成了前面的那个就到后面那个, 和我们刚刚举的收费站例子一样.
使用方法:
这里可以使用dispatch_queue_create来创建对象, 这里需要传入两个参数
.第一个参数: 队列的唯一标识符
第二个参数: 队列的类型, DISPATCH_QUEUE_SERIAL表示串行队列,
DISPATCH_QUEUE_CONCURRENT表示并行队列.
常用方法:
dispatch_after 设置延迟在多少秒后执行
dispatch_get_global_queue 会获取一个全局队列,
dispatch_get_main_queue 会返回主队列
dispatch_once 一次性执行 只执行一次
40 单例
优点: 1.不用再频繁地创建和销毁对象,从而提高了系统的性能和节约系统资源
2. 单例对象可以做到按需创建对象或加载资源,以节省不必要的内存。
缺点: 1. 由于单利模式中没有抽象层接口, 单例类很难再进行扩展
2.单例对象长时间不被利用,系统有可能会认为是垃圾而被回收,这将导致当前单例对象状态的丢失。
41 TableView为什么不响应touchBegan
通过响应链我们不难想象到,当我们点击屏幕时,第一响应者应该是UITableView,而我们调用的touchBegan其实是ViewController的View的方法,所以无法被调用。
UITapGestureRecognizer添加手势实现方法作处理。 手势方法实现
/** 判断当前点击的位置是否处于 collectionView 对象内,如果是,则返回 NO 以使 UITapGestureRecognizer 手势对象失效
if ([touch.view isDescendantOfView:self.collectionView]) {
return NO;
}
return YES;
42 加密方式
MD5 base64 AES DES
网友评论