##表示不同的说法,但都是同一个意思,多看几个说法让自己更好理解
1.block为什么用copy修饰?(从栈copy到堆)
##
默认情况下,block是存档在栈中,可能被随时回收,通过copy操作可以使其在堆中保留一份, 相当于一直强引用着,不会被销毁, 因此如果block中用到self时, 需要将其弱化,
##
相信有很多面试者被问到这样的问题:block使用什么修饰,往往能够答出是copy,很多面试官就会问到:为什么要使用copy,这时候就懵了。
我亲身体验了一把,所以先总结一下。
block本身是像对象一样可以retain,和release。但是,block在创建的时候,它的内存是分配在栈上的,而不是在堆上。他本身的作于域是属于创建时候的作用域,一旦在创建时候的作用域外面调用block将导致程序崩溃。因为栈区的特点就是创建的对象随时可能被销毁,一旦被销毁后续再次调用空对象就可能会造成程序崩溃,在对block进行copy后,block存放在堆区.
使用retain也可以,但是block的retain行为默认是用copy的行为实现的,
因为block变量默认是声明为栈变量的,为了能够在block的声明域外使用,所以要把block拷贝(copy)到堆,所以说为了block属性声明和实际的操作一致,最好声明为copy。
##
函数的声明周期是随着函数调用的结束就终止了。我们的block是写在函数中的。
如果是保存在栈中的block,他会随着函数调用结束被销毁。从而导致我们在执行一个包含block的函数之后,就无法再访问这个block。因为(函数结束,函数栈就销毁了,存在函数里面的block也就没有了),我们再使用block时,就会产生空指针异常。
如果是堆中的block,也就是copy修饰的block。他的生命
周期就是随着对象的销毁而结束的。只要对象不销毁,我们就可以调用的到在堆中的block。
这就是为什么我们要用copy来修饰block。因为不用copy修饰的访问外部变量的block,只在他所在的函数被调用的那一瞬间可以使用。之后就消失了。
2.为什么用block不加__weak会循环引用,block块里面为什么加__strong
##
所谓循环引用,是因为当前控制器在引用着block,而block又引用着self即当前控制器,这样就造成了循环引用。
不加的话结果就是block中引用了self,self引用了block。那么这个时候,如果你不使用weakself,则self和block永远都不会被释放。
__weak可以避免循环引用,但是其会导致外部对象释放了之后,block 内部也访问不到这个对象的问题,我们可以通过在 block 内部声明一个 __strong
的变量来指向 weakObj,使外部对象既能在 block 内部保持住,又能避免循环引用的问题
##
因为block截获self之后self属于block结构体中的一个由__strong修饰的属性会强引用self, 所以需要使用__weak修饰的weakSelf防止循环引用。
block使用的__strong修饰的weakSelf是为了在block(可以理解为函数)生命周期中self不会提前释放。strongSelf实质是一个局部变量(在block这个“函数”里面的局部变量),当block执行完毕就会释放自动变量strongSelf,不会对self进行一直进行强引用。
block有几种
3.kvc的原理
在NSKeyValueCoding中提供了KVC通用的访问方法,分别是getter方法valueForKey:和setter方法setValue:forKey:,以及其衍生的keyPath方法
利用KVC可以修改类的私有变量,可以修改IOS隐藏一些属性,如UITextField的placeHolderText默认style在需求中达不到要求,我们可以直接通过KVC快速定义自己的style,代码如下:
[textField setValue:[UIColor redColor] forKeyPath:@"placeholderLabel.textColor"];
4.kvo的原理
当对象(B)被监听时,那么系统就会在运行期动态的创建该对象类的一个子类,类名就是在该类的前面加上NSKVONotifying_的前缀,子类并重写了任何被监听属性的setter方法,并使用willChangeValueForKey和didChangeValueForKey即手动触发方式来实现,这么做是基于设置属性会调用setter方法(KVC协议)。
并且系统将这个被监听的对象(B)的isa指针指向新生成的子类NSKVONotifying_B
真正调用willChangeValueForKey:和didChangeValueForKey:的是NSKVONotifying_Person子类对象。
5.对GCD的了解,任务分为同步异步,队列串行并发全局队列,用过哪些api
同步:不开启线程 执行完一个任务执行下一个
异步:开启子线程
dispatch_barrier_async
barrier函数可以阻塞任务,执行到他这里,要等之前的任务执行完才能执行之后的任务
dispatch_apply
apply 无序快速遍历,可以用于文件移动等需求
dispatch_after
延时执行
dispatch_group_async
把多个任务加入到队列里面,最后都完成之后会调用Notify的方法进行通知,最终刷新UI
6.runtime 讲一下msgsend
7.手写一个单例
Singleton.h
@interface Singleton : NSObject
+(instancetype) shareInstance;
@end
Singleton.m
@implementation Singleton
static Singleton* instance = nil;
+(instancetype) shareInstance
{
static dispatch_once_t onceToken ;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init] ;
}) ;
return instance ;
}
@end
8.算法
9.用过哪些第三方库
10.线程死锁是什么,怎么解决
11.希望多个任务同时(在多个线程里)执行,再他们都完成之后,再执行其他的任务
可以建立一个分组,让多个任务形成一个组,下面的代码在组中多个任务都执行完毕之后再执行后续的任务:
1 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2 dispatch_group_t group = dispatch_group_create();
3
4 dispatch_group_async(group, queue, ^{ NSLog(@"1"); });
5 dispatch_group_async(group, queue, ^{ NSLog(@"2"); });
6 dispatch_group_async(group, queue, ^{ NSLog(@"3"); });
7 dispatch_group_async(group, queue, ^{ NSLog(@"4"); });
8 dispatch_group_async(group, queue, ^{ NSLog(@"5"); });
9
10 dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"done"); });延迟执行任务
12.SDWbimage缓存图片,图片名
url的md5
13.tableview是怎么计算行高的
14.栈和队列
栈和队列都是动态集合,可以理解为线性表或线性表实现的数据结构。它可以由数组实现,也可以由链表实现。
和数组链表等不一样的是,栈、队列添加、删除数据的位置都是预先设定的。
栈实现的是一种先进后出的策略,队列实现的是一种先进先出的策略。
15.处理图片压缩
16.上传多张图片如何判断图片上传完成
用dispatch_group
17.GET和POST的区别
GET通常用于从服务器获取数据,POST通常用于向服务器发送数据
相同点:
都可以用于请求数据
不同点:
上传参数的方式不一样
GET的参数是追加到url后面
POST的参数是添加在HttpBody中的
对上传的参数的大小限制不一样
GET有限制
POST没有限制
效率不一样
GET效率高于POST
18.autoreleasePool一般你在什么地方使用到?
大循环
在大循环中如果不调用autorelease,会造成内存泄漏
多线程
用于释放子线程
19.简单说一下APP的启动过程,从main文件开始说起
程序启动分为两类:1.有storyboard 2.没有storyboard
有storyboard情况下:
1.main函数
2.UIApplicationMain创建UIApplication对象 创建UIApplication的delegate对象
3.根据Info.plist获得Main.storyboard的文件名,加载Main.storyboard
4.创建UIWindow
5.创建和设置UIWindow的rootViewController
显示窗口没有storyboard情况下:
1.main函数
2.UIApplicationMain创建UIApplication对象 创建UIApplication的delegate对象
3.delegate对象开始处理(监听)系统事件
4.调用application:didFinishLaunchingWithOptions:方法并在其中创建UIWindow
5.创建和设置UIWindow的rootViewController
6.显示窗口
20.防止UIButton重复点击
用runtime
21.static关键字的作用
1.函数体内 static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
2.在模块内的 static 全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
3.在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;
4.在类中的 static 成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
5.在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的static 成员变量。
22.关键字const
const 意味着"只读",下面的声明都是什么意思?
const int a;
int const a;
const int *a;
int * const a;
int const* const a ;
1、2是常量,3是常量指针(指针可变),4是指针常量(常量可变),5是指向常量的常指针
22.当有几个异步的网络请求,希望在它们完成后,再执行操作??
当时回答说用dispatch_group_async,但是group的设计就是为了方便我们执行完一系列的任务之后再执行其他的任务,但是不能忽视的是,这里的任务是有要求的,这里的任务必须要是同步执行的!!如果任务是异步的,group只会执行完任务里面异步之前的代码以及分发异步任务就返回了!!也就代表分发group的当前这个任务完成了!但事实却是这个任务的一部分子任务在其他线程执行了,而且不一定已执行结束返回。
dispatch_semaphore能更好的解决
信号量是基于计数器的一种多线程同步机制,用来管理对资源的并发访问。
我们使用GCD的时候如何让线程同步,也有多种方法
1.dispatch_group
2.dispatch_barrier
3.dispatch_semaphore
dispatch_semaphore有两个主要应用 :
1. 保持线程同步
2. 为线程加锁
dispatch_semaphore相关的3个函数
dispatch_semaphore_create:创建一个Semaphore并初始化信号的总量
dispatch_semaphore_signal:发送一个信号,让信号总量加1
dispatch_semaphore_wait:可以使总信号量减1,当信号总量为0时就会一直等待(阻塞所在线程),否则就可以正常执行。
23. AFN原理
24.WKWebView和UIWebView区别
WKWebView的特性:
在性能、稳定性、功能方面有很大提升,直观体现是内存占用变少;
允许JavaScript的Nitro库加载并使用(UIWebView中限制);
支持了更多的HTML5特性;
高达60fps的滚动刷新率以及内置手势;
将UIWebViewDelegate与UIWebView重构成了14类与3个协议(详见SDK);
25.上线被拒原因
审核期间app内部不能出现任何第三方的名字, 记住是任何第三方! 银行也不行!举一个例子, 某某银行多少点在维护, 这样的文案都不行!
如果A公司没有理财资质, app内部就不能出现任何理财类的字眼
如果可以, 在app描述中加上一句 “app名字 属于 A公司开发运营的”,
认为有bug
26.图片圆角绘制
1.设置layer层的两个属性
//设置圆角imageView.layer.cornerRadius
将多余的部分切掉imageView.layer.masksToBounds =YES;
2.使用贝塞尔曲线UIBezierPath和Core Graphics框架画出一个圆角
3.使用CAShapeLayer和UIBezierPath设置圆角(容易造成离屏渲染)
4.使用带圆形的透明图片.(需要UI做一个切图)
虽然比较笨, 但是不会引发离屏渲染,对内存消耗会比较小。
27.打包的ipa如何瘦身
28.tableview的代理方法调用顺序
1.它会调用代理方法确定有几行
numberOfSectionsInTableView:
2.确定每行的表头高和表尾高(如果设定了HeardView和FooterView)
heightForHeaderInSection:
tableView:heightForFooterInSection:
3.确定每行有多少的cell
numberOfRowsInSection:
4.然后确定每行cell的高度
heightForRowAtIndexPath:
如果有多个section和row则循环执行上面的代码
5.以上信息确定完毕后及调用代理方法去获取cell
cellForRowAtIndexPath:
6.返回cell的高度
heightForRowAtIndexPath:
7.cell将要显示到屏幕上
willDisplayCell:forRowAtIndexPath:
8.cell超出屏幕进行服用时及会调用两次
heightForRowAtIndexPath:
然后在进行调用 5 . 6. 7 方法
11月面试
29.main函数之前做了什么
###
1.动态库链接库
2.ImageLoader加载可执行文件, 里边是被编译过的符号,代码等
3.runtime与+load
#####
简单总结
系统先读取App的可执行文件(Mach-O文件),从里面获得dyld的路径,然后加载dyld,dyld去初始化运行环境。
开启缓存策略,加载程序相关依赖库(其中也包含我们的可执行文件),并对这些库进行链接,最后调用每个依赖库的初始化方法,在这一步,runtime被初始化。
当所有依赖库的初始化后,轮到最后一位(程序可执行文件)进行初始化,在这时runtime会对项目中所有类进行类机构初始化,然后调用所有的load方法。最后dyld返回main函数地址,main函数被调用,我们便来到程序入口main函数。
30.http状态码有哪些
200 - 请求成功
301 - 资源(网页等)被永久转移到其它URL
404 - 请求的资源(网页等)不存在
500 - 内部服务器错误
HTTP状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型,后两个数字没有分类的作用。HTTP状态码共分为5种类型:
分类分类描述
1**信息,服务器收到请求,需要请求者继续执行操作
2**成功,操作被成功接收并处理
3**重定向,需要进一步的操作以完成请求
4**客户端错误,请求包含语法错误或无法完成请求
5**服务器错误,服务器在处理请求的过程中发生了错误
1开头的状态码
100Continue继续。客户端应继续其请求
101Switching Protocols切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到HTTP的新版本协议
2开头的状态码
200OK请求成功。一般用于GET与POST请求
201Created已创建。成功请求并创建了新的资源
202Accepted已接受。已经接受请求,但未处理完成
203Non-Authoritative Information非授权信息。请求成功。但返回的meta信息不在原始的服务器,而是一个副本
204No Content无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档
205Reset Content重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域
206Partial Content部分内容。服务器成功处理了部分GET请求
3开头的状态码
300Multiple Choices多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择
301Moved Permanently永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替
302Found临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI
303See Other查看其它地址。与301类似。使用GET和POST请求查看
304Not Modified未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
305Use Proxy使用代理。所请求的资源必须通过代理访问
306Unused已经被废弃的HTTP状态码
307Temporary Redirect临时重定向。与302类似。使用GET请求重定向
4开头的状态码
400Bad Request客户端请求的语法错误,服务器无法理解
401Unauthorized请求要求用户的身份认证
402Payment Required保留,将来使用
403Forbidden服务器理解请求客户端的请求,但是拒绝执行此请求
404Not Found服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置”您所请求的资源无法找到”的个性页面
405Method Not Allowed客户端请求中的方法被禁止
406Not Acceptable服务器无法根据客户端请求的内容特性完成请求
407Proxy Authentication Required请求要求代理的身份认证,与401类似,但请求者应当使用代理进行授权
408Request Time-out服务器等待客户端发送的请求时间过长,超时
409Conflict服务器完成客户端的PUT请求是可能返回此代码,服务器处理请求时发生了冲突
410Gone客户端请求的资源已经不存在。410不同于404,如果资源以前有现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置
411Length Required服务器无法处理客户端发送的不带Content-Length的请求信息
412Precondition Failed客户端请求信息的先决条件错误
413Request Entity Too Large由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个Retry-After的响应信息
414Request-URI Too Large请求的URI过长(URI通常为网址),服务器无法处理
415Unsupported Media Type服务器无法处理请求附带的媒体格式
416Requested range not satisfiable客户端请求的范围无效
417Expectation Failed服务器无法满足Expect的请求头信息
5开头的状态码
500Internal Server Error服务器内部错误,无法完成请求
501Not Implemented服务器不支持请求的功能,无法完成请求
502Bad Gateway充当网关或代理的服务器,从远端服务器接收到了一个无效的请求
503Service Unavailable由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中
504Gateway Time-out充当网关或代理的服务器,未及时从远端服务器获取请求
505HTTP Version not supported服务器不支持请求的HTTP协议的版本,无法完成处理
31.get请求中 注意事项
1.不要传中文
get请求建议尽量不带中文参数,如果使用建议使用两次encodeURI进行编码
2.url出现了有+,空格,/,?,%,#,&,=等特殊符号的时候,可能在服务器端无法获得正确的参数值,如何是好?
解决办法
将这些字符转化成服务器可以识别的字符,对应关系如下:
URL字符转义
用其它字符替代吧,或用全角的。
+ URL 中+号表示空格 %2B
空格 URL中的空格可以用+号或者编码 %20
/ 分隔目录和子目录 %2F
? 分隔实际的URL和参数 %3F
% 指定特殊字符 %25
# 表示书签 %23
& URL 中指定的参数间的分隔符 %26
= URL 中指定参数的值 %3D
32.NSSet和NSArray区别
*NSSet: (内部是hash 字典内部也是hash)
1>无序的、不重复的。存放到NSSet中的内容并不会排序与添加顺序也没有关系
2>通过anyObject来访问单个元素
3>遍历NSSet中的每个元素。通过forin循环来遍历
4>好处:效率高。
5>应用场景:
比如重用Cell的时候,从缓存池中随便获取一个就可以了,无需按照指定顺序来获取
当需要把数据存放到一个集合中,然后判断集合中是否有某个对象的时候
*NSArray
1>有序的、可以有重复对象。对象的顺序是按照添加的顺序来保存的
2>好处:有序访问
3>应用场景:在绝大多数需要依赖顺序的情况下(比如tableView的数据源集合,在实际操作中要根据下标来获取对象)
3>通过下标来访问
33.判断一个数组中是否包含某个元素
NSString *str = @"数组";
NSArray *array=@[@"who",@"数组",@"array",@"3"];
BOOL isbool = [array containsObject: str];
36.分类添加了一个属性 能编译通过吗,运行呢
Category 是表示一个指向分类的结构体的指针,结构体没有属性列表:这也就是为什么分类不能添加属性的本质原因。
分类只能通过动态关联对象的方法给外界封装一个假的实例变量,事实上这个变量在类结构中不存在。
一句话就是 分类 不会生成 带下划线 的 成员变量 和 set get 方法。 由于OC是动态语言,可以通过runtime手动添加setter/getter方法 关联对象void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
34.扩大button的点击范围
分类(runtime用关联对象添加属性 、重新点击范围、分类的方法覆盖重写了- (UIView*) hitTest:(CGPoint) point withEvent:(UIEvent*) event)
通过OC创建类别的方式,运用Runtime关联对象的方法,对UIButton进行了扩展 ,创建一个方法直接用于扩大Button的点按区域,而不改变它的显示内容,使代码的可读性大大增强。
其执行原理为:OC中创建类别(Categroy)的方式,并不允许给已有的类扩展属性,只可以给其扩展方法。所以,需要使用Runtime“黑魔法”中的关联对象(Associative Object)的一些方法,动态地为某个button对象添加扩展距离的属性,然后检测UITouch事件的触摸点是否在我们扩展距离后Rect内,从而达到想要的效果。
首先,创建一个UIButton的Category,起名为EnlargeTouchArea,设置一个外界可访问的方法setEnlaEdgeWithTop:right:bottom:left,在使用时也只需使用这个方法即可,传入的四个参数分别是上、右、下、左的扩展距离。
第一种:重写方法 -(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event
第二种: 重写方法 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
35.青花瓷为什么加了证书能看到https里的内容 ,抓包https的原理是什么?
这类抓包工具都是把自己设置为系统代理服务器,这样你所有的请求都会通过它发起,这样你就能抓到每个请求和应答包了。
HTTPS的代理基本原理是“中间人攻击”,代理服务器对请求发起方把自己伪装成目标网站,对目标网站则把自己模拟为客户端,这样就需要客户端(比如浏览器)承认代理的证书就是目标网站的证书,安装的证书就是干这个的。
Charles本身是一个协议代理工具,如果只是普通的HTTP请求,因为数据本身没经过再次加密,因此作为代理可以知道所有客户端发送到服务端的请求内容以及服务端返回给客户端的数据内容,这也就是抓包工具能够将数据传输内容直接展现出来的原因。对于HTTPS请求,4,6,8步骤的数据都已经经过了加密,代理如果什么都不做的话是无法获取到其中的内容的。为了实现这个过程的数据获取,Charles需要做的事情是对客户端伪装服务端,对服务端伪装客户端,具体:
截获真实客户端的HTTPS请求,伪装客户端向真实服务端发送HTTPS请求
接受真实服务器响应,用Charles自己的证书伪装服务端向真实客户端发送数据内容
一般情况下HTTPS中是客户端对服务端做证书校验,当然也有一些金融机构会有用户证书作为提供给服务端做用户认证的工具,保证发出请求的的确是这部分授权用户。
36.字节数
Objective-c主要分为四种基本数据类型:
整型:short int (内存中占16位); 2个字节(Mac中) 2个字节(ios)
int(内存中占32位); 4 个字节(Mac中)4个字节(ios)
long int(内存中占64位); 8个字节(Mac中) 4个字节(ios)
long long(内存中占64位); 8个字节(Mac中) 8个字节(ios)
字符型:(0--255) 8位无符号整数 1个字节(不支持中文)
浮点型:float :4个字节
double:8个字节
long double:16个字节
枚举型:
enum season{spring,summer,fall,winter};
BOOL:yes(1)和no(0)
类型转换:
1.所有的short char 都将提升到int型
2.short - int - long - long long - float -double - long doube (低 ->高)
37.怎么判断cell是否在屏幕内
iOS 判断 UITableViewCell 和 UICollectionViewCell 是否移出屏幕
关于判断 UITableViewCell 是否移出屏幕,可以先了解怎么判断 Cell 在屏幕中
1. UITableView 有两个关于 Cell可用的属性
@property (nonatomic, readonly) NSArray<__kindof UITableViewCell *> *visibleCells;
@property (nonatomic, readonly, nullable) NSArray<NSIndexPath *> *indexPathsForVisibleRows;
使用两个属性可以获取到 UITableView 的可视范围内的相关属性,一 个是 Cell 的数组还有一个是 NSIndexPath 数组.不过在实际操作中发现使用这两个属性来做为判断依据不是非常准确,所以放弃使用.
2. 第二个就是从 frame 相关信息入手,对于一个 Cell 来说,大概的关系是UIView(即 VC 视图) -> UITableView ->UITableViewCell, 就是说先将 Cell 在 UITableView 中的位置找到,然后通过转换函数转换成在 VC 视图的位置,这种方法是比较准确,暴力的方法,所以使用该方法来处理我们需要的业务逻辑.即
CGRect rectInTableView = [_tableView rectForRowAtIndexPath: self.operatedCellIndexPath];
CGRect rectInSuperview = [_tableView convertRect:rectInTableView toView:[_tableView superview]];
到这里,我们的需求基本上已经完成了,剩下需要做的就是记录下我们需要操作的 Cell, 因为我们判断 Cell 移出屏幕,肯定是对已经操作过的 Cell 进行下一步处理,比如视频的暂停或者停止播放等等.这样可以写个 Block,在加载 Cell 的时候记录下我们操作的 Cell,本文就是在播放视频的时候,调用 block 记录下当前的 Cell,并且停止正在播放的 Cell 中的 VideoPlayer.
WEAKSELF
cell.startPlayVideoAction = ^(){
STRONGSELF
// 这个地方可以对上一次记录的 Cell 和 IndexPath 进行处理,比如我就可以把正在播放的视频停掉,类似这样
// 记录 当前被点击 cell 的位置和 indexPath
strongSelf.operatedCellIndexPath = indexPath;
strongSelf.currentPlayingCell = [strongSelf.tableView cellForRowAtIndexPath: indexPath];
};
最后一步就是在 UITableView 或者 UICollectionView 滑动的时候进行监听了
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
// 这里我记录了 Cell 的 IndexPath 和 Cell
if ( self.operatedCellIndexPath != nil ) {
CGRect rectInTableView = [self.tableView rectForRowAtIndexPath: self.operatedCellIndexPath];
CGRect rectInSuperview = [self.tableView convertRect:rectInTableView toView:[self.tableView superview]];
if ( rectInSuperview.origin.y > SCREEN_HEIGHT || rectInSuperview.origin.y + rectInSuperview.size.height < 0 ) {
// 对已经移出屏幕的 Cell 做相应的处理
}
}
}
38.UIView和CALyaer区别
39.谷歌怎么翻墙的
40.nil和NULL的区别 Nil [NSNull null]
nil是一个对象值,如果要把一个对象设置为空的时候就用nil。
Nil是一个类对象的值,如果要把一个Class类型的对象设置为空的时候就用Nil。(在iOS中,Nil完全等同于nil。)
NULL是一个通用指针,NUll就是C语言中的一个空指针,在Objective-C中也可以使用。
[NSNull null]是值为空的对象,和nil、Nil、Null是不等价的。
最后再解释一下,空对象和值为空的对象的区别:
“空对象”是已经释放了内存地址的对象,即不存在的对象。
“值为空的对象”是分配了地址,但是没有值得对象,是实际存在的对象。
给nil发消息会发生什么
objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,然后在发送消息的时候,objc_msgSend方法不会返回值,所谓的返回内容都是具体调用时执行的。那么,回到本题,如果向一个nil对象发送消息,首先在寻找对象的isa指针时就是0地址返回了,所以不会出现任何错误。
41.通知可以不销毁吗,如果不销毁会发生什么
简单粗暴的回答:有时候会导致crash
一般的通知,如果确定通知是一次性的,那么在通知事件完成之后还是删除比较好。不过不移除不会发生什么问题。但是对于有些通知,不移除可能会造成崩溃,比如在你通知事件中处理数据或UI事件,但是由于通知的的不确定性造成处理事件的时间不确定,有异步操作在通知事件中处理等都可能造成崩溃。
现在只发一次不代表以后不会发送多次。你现在很清楚不代表以后接手的人也能清楚。等问题爆发出来就等着抓瞎吧.不移除以后一定会崩溃
而且通知的崩溃是相当难检测的
iOS 开发中, A控制器push到B控制器之后,A控制器会被销毁吗?B控制器pop之后,B控制器会被销毁吗?
A push B后,A一定不会销毁。B pop之后,如果B没有被其它指针retain,那么B会被销毁,销毁的标志是:会走B的dealloc方法。
同理,引出了我们今天所说的通知:如果A监听了通知,并且将移除通知的代码写在了dealloc方法中,那么 A控制器push到B控制器之后,在A中监听的通知并不会移除,因为并不会走dealloc方法。当然,若想强制性的让A控制器push到B控制器之后销毁A中监听的通知,可以将移除的代码写在viewWillDisappear方法中(也就是下面的写法2).
总结:push操作不会走dealloc方法,pop操作才会走dealloc方法。push操作不会销毁通知,pop操作才会销毁通知。
42.交换方法是建一个什么类 NSObject
load
init
initialize
load 方法会在类被 import 时调用一次
+load会在类初始加载时调用,+initialize会在第一次调用类的类方法或实例方法之前被调用。然后是init
43.copy修饰 NSArray ,copy能修饰NSMutableArray吗?为什么?
用 strong修饰NSMutableArray
NSMutableArray用copy修饰之后,在使用addObjectsFromArray方法时崩溃
error:[__NSFrozenArrayM addObjectsFromArray:]: unrecognized selector
这个错误误导点:ArrayM这个让开发者认为是可变数组。但是因为你是用copy修饰的,所以这个数组其实是一个不可变数组。
这个问题主要是误写导致的,只需要把copy改成strong。
44.自动释放池的原理
autorelease 本质上就是延迟调用 release
-[NSAutoreleasePool release] 方法最终是通过调用 AutoreleasePoolPage::pop(void *) 函数来负责对 autoreleasepool 中的 autoreleased 对象执行 release 操作的。
那这里的 AutoreleasePoolPage 是什么东西呢?其实,autoreleasepool 是没有单独的内存结构的,它是通过以 AutoreleasePoolPage 为结点的双向链表来实现的。我们打开 runtime 的源码工程,在 NSObject.mm 文件的第 438-932 行可以找到 autoreleasepool 的实现源码。通过阅读源码,我们可以知道:
每一个线程的 autoreleasepool 其实就是一个指针的堆栈;
每一个指针代表一个需要 release 的对象或者 POOL_SENTINEL(哨兵对象,代表一个 autoreleasepool 的边界);
一个 pool token 就是这个 pool 所对应的 POOL_SENTINEL 的内存地址。当这个 pool 被 pop 的时候,所有内存地址在 pool token 之后的对象都会被 release ;
这个堆栈被划分成了一个以 page 为结点的双向链表。pages 会在必要的时候动态地增加或删除;
Thread-local storage(线程局部存储)指向 hot page ,即最新添加的 autoreleased 对象所在的那个 page 。
一个空的 AutoreleasePoolPage 的内存结构如下图所示:
NSAutoreleasePool实际上是个对象引用计数自动处理器,在官方文档中被称为是一个类。
NSAutoreleasePool可以同时有多个,它的组织是个栈,总是存在一 个栈顶pool,也就是当前pool,每创建一个pool,就往栈里压一个,改变当前pool为新建的pool,然后,每次给pool发送drain消 息,就弹出栈顶的pool,改当前pool为栈里的下一个 pool。
45.class结构体包含了什么,NSObject包含什么
消息转发
1.看是否能够动态的添加一个方法,
注意这是一个类方法,因为是向接收者所属的类进行请求。
+(BOOL)resolveInstanceMethod:(SEL)name
重写了类方法+ (BOOL)resolveInstanceMethod:(SEL)name,当找不到相关实例方法的时候就会调用该类方法去询问是否可以动态添加,如果返回True就会再次执行相关方法,
2.第二次机会: 备援接收者
当对象所属类不能动态添加方法后,runtime就会询问当前的接受者是否有其他对象可以处理这个未知的selector,相关方法声明如下:
- (id)forwardingTargetForSelector:(SEL)aSelector;
第三次机会: 消息重定向
当没有备援接收者时,就只剩下最后一次机会,那就是消息重定向。这个时候runtime会将未知消息的所有细节都封装为NSInvocation对象,然后调用下述方法:
- (void)forwardInvocation: (NSInvocation*)invocation;
isa: 指向元类的objc_class结构体指针,iOS中的类也是对象,元类中储存有类对象的类方法;
superclass: 指向父类的objc_class结构体指针,可以通过父类的指针找到变量和方法;
name: 类名;
version: 版本号,默认为0
info: 其他信息,运行期间的一些位标示
instance_size:类实例变量大小
46.组件化有了解吗
47.UIview的继承关系
UIWindow和UILabel都是UIView的子类
UIButton是继承自UIControl
48宏的本质
49、加锁问题,NSLock和信号量什么情况下用 ,同步锁能用递归里面吗(用递归外面?),syn和dispacth_once什么区别 为什么单例不用syn呢
lock的优势在于
1,更自由,不限制于锁加于类,方法上,可以跨方法持有锁
2,支持轮询锁,定时锁,可中断锁等
synchronized的优势在于
1,用法简单直接,原语关键字
2,不会产生gc垃圾。
两种锁的底层实现
Synchronized:底层使用指令码方式来控制锁的,映射成字节码指令就是增加来两个指令:monitorenter和monitorexit。当线程执行遇到monitorenter指令时会尝试获取内置锁,如果获取锁则锁计数器+1,如果没有获取锁则阻塞;当遇到monitorexit指令时锁计数器-1,如果计数器为0则释放锁。
Lock:底层是CAS乐观锁,依赖AbstractQueuedSynchronizer类,把所有的请求线程构成一个CLH队列。而对该队列的操作均通过Lock-Free(CAS)操作。
Synchronized和Lock比较
Synchronized是关键字,内置语言实现,Lock是接口。
Synchronized在线程发生异常时会自动释放锁,因此不会发生异常死锁。Lock异常时不会自动释放锁,所以需要在finally中实现释放锁。
Lock是可以中断锁,Synchronized是非中断锁,必须等待线程执行完成释放锁。
Lock可以使用读锁提高多线程读效率。
@synchronized采用的是递归互斥锁来实现线程安全,而dispatch_once的内部则使用了很多原子操作来替代锁,以及通过信号量来实现线程同步,而且有很多针对处理器优化的地方,甚至在if判断语句上也做了优化(逼格有点高),使得其效率有很大的提升,虽然其源码很短,但里面包含的东西却很多,所以苹果也推荐使用dispatch_once来创建单例。
@synchronized指令实现锁的优点就是我们不需要在代码中显式的创建锁对象,便可以实现锁的机制,但作为一种预防措施,@synchronized块会隐式的添加一个异常处理例程来保护代码,该处理例程会在异常抛出的时候自动的释放互斥锁。
网友评论