准备找工做的你,能够看看,复习复习!!web
在动态运行下咱们能够构建任何一个类,而后咱们经过这个类知道这个类的全部的属性和方法,而且若是咱们建立一个对象,咱们也能够经过对象找到这个类的任意一个方法,这就是反射机制。
好比NSClassFormString,NSStringFormSelector,NSSelectorFormString
参考连接面试
参考连接
block与函数相似,只不过是直接定义在另外一个函数里,和定义它的那个函数共享同一个范围内的东西,
block的强大之处是:在声明它的范围里,全部变量均可觉得其捕获,这也就是说,那个范围内的所有变量,在block依然能够用,默认状况下,为block捕获的变量,是不能够在block里修改的,不过声明的时候能够加上__block修饰符,这样就能够再block内修改了。算法
block自己和其余对象同样,有引用计数,当最后一个指向block的引用移走以后,block就回收了,回收时也释放block所捕获的变量。swift
Block的实现是经过结构体的方式实现,在编译的过程当中,将Block生成对应的结构体,在结构体中记录Block的匿名函数,以及使用到的自动变量,在最后的使用中,经过Block结构体实例访问成员中存放的匿名函数地址调用匿名函数,并将自身做为参数传递。
block其实就是C语言的扩充功能,实现了对C的闭包实现,一个带有局部变量的匿名函数,
block的本质也是一个OC对象,它内部也有一个isa指针,block是封装了函数调用以及函数调用环境的OC对象,为了保证block内部可以正常访问外部的变量,block有一个变量捕获机制。static 修饰的变量为指针传递,一样会被block捕获。局部变量由于跨函数访问因此须要捕获,全局变量在哪里均可以访问 ,因此不用捕获。
当block内部访问了对象类型的auto变量时,若是block在栈上,block内部不会对变量产生强应用,不论block的结构体内部的变量时__strong修饰仍是__weak修饰,都不会对变量产生强引用
默认状况下block不能修改外部的局部变量数组
static修饰的age变量传递到block内部的是指针,在__main_block_func_0函数内部就能够拿到age变量的内存地址,所以就能够在block内部修改age的值。浏览器
有三种类型缓存
__NSGlobalBlock__ ( _NSConcreteGlobalBlock )
__NSStackBlock__ ( _NSConcreteStackBlock )
__NSMallocBlock__ ( _NSConcreteMallocBlock )
复制代码
__block内存管理安全
当block内存在栈上时,并不会对__block变量产生内存管理,当block被copy到堆上时会调用block内部的copy函数,copy函数内部会滴啊用_Block_object_assign函数,_Block_object_assign函数会对__block变量造成强引用(至关于retain)。
当block被copy到堆上时,block内部引用的__block变量也会被复制到堆上,而且持有变量,若是block复制到堆上的同时,__block变量已经存在堆上了,则不会复制。bash
当block从堆中移除的话,就会调用dispose函数,也就是__main_block_dispose_0函数,__main_block_dispose_0函数内部会调用_Block_object_dispose函数,会自动释放引用的__block变量。服务器
解决循环引用问题
使用__weak和__unsafe_unretained修饰符合一解决循环引用的问题,__weak会使block内部将指针变为弱指针。
__weak 和 __unsafe_unretained的区别。
__weak不会产生强引用,指向的对象销毁时,会自动将指针置为nil
__unsafe_unretained不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变
__strong 和 __weak
在block内部从新使用__strong修饰self变量是为了在block内部有一个强指针指向weakSelf避免在block调用的时候weakSelf已经被销毁。
__block用于解决block内部不能修改auto变量值的问题,__block不能修饰静态变量和全局变量
_block 所起到的做用就是只要观察到该变量被 block 所持有,就将“外部变量”在栈中的内存地址放到了堆中。进而在block内部也能够修改外部变量的值。
做为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个个人点击加入群聊iOS交流群:789143298 ,无论你是小白仍是大牛欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 你们一块儿交流学习成长!
![]()
NSDictionary是使用hash表来实现key和vaLue之间的映射和存储的
hash原理
hash概念:哈希表的本质是一个数组,数组中没一个元素称为一个箱子,箱子中存放的是键值对。
哈希表的存储过程:
1.根据key计算出它的哈希值h
2.假设箱子的个数为n,那么这个键值对应应该在第(h % n)个箱子中。
3.若是该箱子中已经有了键值对,就使用开放寻址法或者拉链法解决冲突。
在使用拉链法解决哈希冲突时,每一个箱子实际上是一个链表,属于同一个箱子的全部键值对都会排列在链表中。
哈希表还有一个重要的属性:负载因子(load factor),它用来衡量哈希表的空/满程度,必定程度上也能够体现查询的效率,计算公式为:
NSCache是一个容器,相似于NSDictionary,经过key-value形式存储和查询值,用于临时存储对象。
NSCache赛过NSDictionary之处在于,当系统资源将要耗尽时,它能够自动删减缓存。若是采用普通的字典,那么就要本身编写挂钩,在系统发出“低内存”通知时手工删减缓存。
NSCache并不会“拷贝”键,而是会“保留”它。此行为用NSDictionary也能够实现,然而须要编写至关复杂的代码。NSCache对象不拷贝键的缘由在于:不少时候,键都是不支持拷贝操做的对象来充当的。所以,NSCache不会自动拷贝键,因此说,在键不支持拷贝操做的状况下,该类用起来比字典更方便。另外,NSCache是线程安全的,而NSDictionary则绝对不具有此优点。
属性是OC的一项特性,用于封装对象的数据,OC对象一般会把其所须要的数据保存为各类实例对象,实例对象通常经过存取方法来访问,其中获取方法用于读取变量值,而设置方法用于写入变量值,开发者能够令编译器自动编写与属性相关的存取方法。
objc_msgSend叫作消息传递,消息有名称或选择子,能够接受参数
Runtime时执行的流程是这样的:
一个对象的方法像这样[obj foo],编译器转成消息发送objc_msgSend(obj, foo),Runtime时执行的流程是这样的:
首先,经过obj的isa指针找到它的 class ;
在 class 的 method list 找 foo ;
若是 class 中没到 foo,继续往它的 superclass 中找 ;
一旦找到 foo 这个函数,就去执行它的实现IMP 。
指针常量:(指针变量前加const) int *const p;指针自己是一个常量。在声明的时候初始化,里面的值(存放的地址)不能更改。
常量指针:(在类型前加const) const int *p;指针自己是一个变量,初始化是最好给一个常量的地址,它里面值(存放的地址)能够改变。
NSNotification:这是一个包装通知信息的类,相似一个model,存储了notificationName,object,userInfo等信息.
NSNotificationCenter:顾名思义~通知中心,就是用来管理通知的接收和发送的类.
1.定义一个类TestNotification,用来存储notificationName,object,userInfo等信息.这里咱们仿照系统的API进行设计.这里另外加了两个参数observer和selector.
2.设计通知中心类TestNotificationCenter,一样仿照系统的API进行设计.
KVO是基于runtime机制实现的
当某个类的属性对象第一次被观察时,系统就会在运行期动态地建立该类的一个派生类,在这个派生类中重写基类中任何被观察属性的setter 方法。派生类在被重写的setter方法内实现真正的通知机制
若是原类为Person,那么生成的派生类名为NSKVONotifying_Person
每一个类对象中都有一个isa指针指向当前类,当一个类对象的第一次被观察,那么系统会偷偷将isa指针指向动态生成的派生类,从而在给被监控属性赋值时执行的是派生类的setter方法
键值观察通知依赖于NSObject 的两个方法: willChangeValueForKey: 和 didChangevlueForKey:;在一个被观察属性发生改变以前, willChangeValueForKey:必定会被调用,这就 会记录旧的值。而当改变发生后,didChangeValueForKey:会被调用,继而 observeValueForKey:ofObject:change:context: 也会被调用。
补充:KVO的这套实现机制中苹果还偷偷重写了class方法,让咱们误认为仍是使用的当前类,从而达到隐藏生成的派生类
KVC底层实现原理(以下)
KVC运用了一个isa-swizzling技术. isa-swizzling就是类型混合指针机制, 将2个对象的isa指针互相调换, 就是俗称的黑魔法.
KVC主要经过isa-swizzling, 来实现其内部查找定位的. 默认的实现方法�由NSOject提供isa指针, 如其名称所指,(就是is a kind of的意思), 指向分发表对象的类. 该分发表实际上包含了指向实现类中的方法的指针, 和其它数据。
首先搜索setKey:方法.(key指成员变量名, 首字母大写)
二、上面的setter方法没找到, 若是类方法accessInstanceVariablesDirectly返回YES. 那么按 _key, _isKey,key, iskey的顺序搜索成员名.(NSKeyValueCodingCatogery中实现的类方法, 默认实现为返回YES)
三、若是没有找到成员变量, 调用setValue:forUnderfinedKey:
说说你理解weak属性?
Runtime维护了一个weak表,用于存储指向某个对象的全部weak指针。weak表实际上是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象的地址)数组。
一、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。
二、添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的做用是更新指针指向,建立对应的弱引用表。
三、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取全部weak指针地址的数组,而后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。
追问的问题一:
runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址做为 key,当此对象的引用计数为 0 的时候会 dealloc,假如 weak 指向的对象内存地址是 a ,那么就会以 a 为键, 在这个 weak 表中搜索,找到全部以 a 为键的 weak 对象,从而设置为 nil 。
追问的问题二:
在C和OC中,你一直在处理指针,指针无非是存储另外一个变量的内存地址的变量。当向一个对象发送消息时,指向该对象的指针将会被引用,这意味着,你获取了指针所指的内存地址,并访问该存储区域的值。
当该存储器区域再也不映射到你的应用时,或者换句话说,该内存区域在你认为使用的时候没有使用,该内存区域是没法访问的,这时内核会抛出一个异常(EXC),代表你的应用程序不能访问该存储器区域(BAD_ACCESS).
当你碰到BAD_ACCESS,这意味着你试图发送消息到的内存块,但内存块没法执行该消息。可是,在某些状况下,BAD_ACCESS是由被损坏的指针引发的,每当你的应用程序尝试引用损坏的指针,一个异常就会被内核抛出。
调试请看
int a = 10,b = 20; a = a+b; b = a - b; a = a - b; //第二种方法,位异或运算 a = a^b; b = a^b; a = a^b; //第三种方法,使用指针 int *pa = &a; int *pb = &b; *pa = b; *pb = a; NSLog(@"after,a = %d",a); NSLog(@"after,b = %d",b); 复制代码
int sum(int n) { if (n==1) return 1; else return sum(n-1)+n; } 复制代码
Category不能添加成员变量,能够添加属性,可是属性要手动实现setter和getter方法。
Category的原理
简单地说就是经过runtime动态的吧Category中的方法等添加到类中,
从category的定义也能够看出category的可为(能够添加实例方法,类方法,甚至能够实现协议,添加属性)和不可为(没法添加实例变量)。
通过编译的类在程序启动后就被runtime加载,没有机会调用addIvar。程序在运行时动态构建的类须要在调用objc_registerClassPair
以后才能够被使用,一样没有机会再添加成员变量。
category为何只能添加方法
由于方法和属性并不“属于”类实例,而成员变量“属于”类实例。咱们所说的“类实例”概念,指的是一块内存区域,包含了isa指针和全部的成员变量。因此假如容许动态修改类成员变量布局,已经建立出的类实例就不符合类定义了,变成了无效对象。但方法定义是在objc_class中管理的,无论如何增删类方法,都不影响类实例的内存布局,已经建立出的类实例仍然可正常使用。
Category注意事项:
一、category的方法没有“彻底替换掉”原来类已经有的方法,也就是说若是category和原来类都有methodA,那么category附加完成以后,类的方法列表里会有两个methodA
二、category的方法被放到了新方法列表的前面,而原来类的方法被放到了新方法列表的后面,这也就是咱们日常所说的category的方法会“覆盖”掉原来类的同名方法,这是由于运行时在查找方法的时候是顺着方法列表的顺序查找的,它只要一找到对应名字的方法,就会罢休_,却不知后面可能还有同样名字的方法。
详细请点击
runloop 正如其名,loop是一种循环,和run放在一块儿就是表示一直在运行着循环,实际上Runloop和线程是紧密相连的,能够这样说run loop是为了线程而生,没有线程,它就没有存在必要。每一个线程,包括程序的主线程( main thread )都有与之相应的 run loop 对象。
主线程是默认开启的,其余线程须要手动开启
autoreleasePool自动释放池是OC的一种内存自动回收机制,它能够延时加入autoreleasePool中的变量release的时机,在正常状况下,建立的变量会在超出其做用于的时候release,可是若是将变量加入autoreleasePool,那么release将延迟执行。
AutoreleasePool建立是在一个RunLoop事件开始以前(push),AutoreleasePool释放是在一个RunLoop事件即将结束以前(pop)。
AutoreleasePool里的Autorelease对象的加入是在RunLoop事件中,AutoreleasePool里的Autorelease对象的释放是在AutoreleasePool释放时。
单个自动释放池的执行过程就是objc_autoreleasePoolPush() —> [object autorelease] —> objc_autoreleasePoolPop(void *)
代理(delegate)的主旨是:定义一套接口,某个对象若想接受另外一个对象的委托,则需听从此接口,以便成为其委托对象,而这另外一个对象的委托,则需听从此接口,以便成为其委托对象,而这另外一个对象则能够给其委托对象回传一些信息,也能够发生相关事件时通知委托对象。
注意delegate需定义成weak。由于二者之间必须为"非拥有关系",一般状况下,扮演delegate的那个对象也要持有本对象。
1.UITableViewCell重用机制?
UITableView只会建立一屏幕(或者一屏幕多一点)的cell,其余都是取出来重用的。每当cell滑出屏幕的时候,就会放到一个集合中,当要显示某一位置的cell时,会先去集合中取,有的话,就直接拿出来显示,没有在建立。
2.tableView滑动为何会卡顿?
cell赋值内容时,会根据内容设置布局,也就能够知道cell的高度,如有1000行,就会调用1000次 cellForRow方法,而咱们对cell的处理操做,都是在这个方法中赋值,布局等等,开销很大。
3.优化方法?
3.1优化:heightForRow方法处理cell高度。
思路:赋值和计算布局分离。cellForRow负责赋值,heightRorRow负责计算高度。
3.2自定义cell绘制:
各个信息都是根据以前算好的布局进行绘制的。须要异步绘制。重写draeRect方法就不须要异步绘制了,由于drawRect原本就是异步绘制的。图文混排的绘制,coreText绘制。
3.3按需加载(UIScrollView方面):
若是目标行与当前行相差超过指定行数,只在目标滚动范围的先后制定n行加载。滚动很快时,只加载目标范围内得cell,这样按需加载,极大地提升了流畅性。
4.总结
1.提早计算并缓存好高度,由于heightForRow最频繁的调用。
2.异步绘制,遇到复杂界面,性能瓶颈时,多是突破口。
3.滑动时按需加载,这个在大量图片展现,网络加载时,很管用。(SDWebImage已经实现异步加载)。
4.重用cells。
5.若是cell内显示得内容来自web,使用异步加载,缓存结果请求。
6.少用或不用透明图层,使用不透明视图。
7.尽可能使全部的view opaque,包括cell自己。
8.减小subViews
9.少用addView给cell动态添加view,能够初始化的时候就添加,而后经过hide控制是否显示。
精确试试
2三、说一下HTTP协议以及常用的code码的含义。
不清楚 有知道的能够回答下
HTTP协议:即超文本传输协议,是一种详细规定了浏览器和万维网服务器之间互相通讯的规则,经过因特网传送万维网文档的数据传送协议
HTTP协议做用:HTTP协议是用于从www服务器传输超文本到本地浏览器的传送协议,它可使浏览器更加高效,使网络传输减小,它不只保证计算机正确快速的传输超文本文档,还肯定传输文档的哪一部分,以及哪部份内容首先显显示等。
URL:咱们在浏览器的地址栏里输入的网站地址叫作URL (Uniform Resource Locator,统一资源定位符)。就像每家每户都有一个门牌地址同样,每一个网页也都有一个Internet地址。当你在浏览器的地址框中输入一个URL或是单击一个超级连接时,URL就肯定了要浏览的地址。浏览器经过超文本传输协议(HTTP),将Web服务器上站点的网页代码提取出来,并翻译成漂亮的网页。
HTTPS::是以安全为目标的HTTP通道,简单讲HTTP的安全版,即HTTP下加入SSL层,HTTPS的安全基础是SSL,所以加密的详细内容就须要SSL。
在HTTP/1.1协议中,定义了8种发送HTTP请求的方法
GET、POST、OPTIONS、HEAD、PUT、DELETE、TRACE、CONNECT、PATCH
各个方法的解释以下(全部方法全为大写):
GET: 请求获取Request-URI所标识的资源
POST: 在Request-URI所标识的资源后附加新的数据
HEAD: 请求获取由Request-URI所标识的资源的响应消息报头
PUT: 请求服务器存储一个资源,并用Request-URI做为其标识
DELETE: 请求服务器删除Request-URI所标识的资源
TRACE: 请求服务器回送收到的请求信息,主要用于测试或诊断
CONNECT: 保留未来使用
OPTIONS: 请求查询服务器的性能,或者查询与资源相关的选项和需求
根据HTTP协议的设计初衷,不一样的方法对资源有不一样的操做方式
PUT :增
DELETE :删
POST:改
GET:查
最经常使用的是GET和POST(实际上GET和POST都能办到增删改查)
详细内容
像UIKit这样大的框架上确保线程安全是一个重大的任务,会带来巨大的成本。UIKit不是线程安全的,假如在两个线程中设置了同一张背景图片,颇有可能就会因为背景图片被释放两次,使得程序崩溃。或者某一个线程中遍历找寻某个subView,然而在另外一个线程中删除了该subView,那么就会形成错乱。apple有对大部分的绘图方法和诸如UIColor等类改写成线程安全可用,可仍是建议将UI操做保证在主线程中。