1.weak和assign区别
修饰变量类型的区别:
weak 只可以修饰对象。如果修饰基本数据类型,编译器会报错-“Property with ‘weak’ attribute must be of object type”。
assign 可修饰对象和基本数据类型。当需要修饰对象类型时,MRC时代使用unsafe_unretained。当然,unsafe_unretained也可能产生野指针,所以它名字是"unsafe_”。
是否产生野指针的区别:
weak 不会产生野指针问题。因为weak修饰的对象释放后(引用计数器值为0),指针会自动被置nil,之后再向该对象发消息也不会崩溃。 weak是安全的。
assign 如果修饰对象,会产生野指针问题;如果修饰基本数据类型则是安全的。修饰的对象释放后,指针不会自动被置空,此时向对象发消息会崩溃。
总结:
assign 适用于基本数据类型如int,float,struct等值类型,不适用于引用类型。因为值类型会被放入栈中,遵循先进后出原则,由系统负责管理栈内存。而引用类型会被放入堆中,需要我们自己手动管理内存或通过ARC管理。
weak 适用于delegate和block等引用类型,不会导致野指针问题,也不会循环引用,非常安全。
值类型会被放入栈中,遵循先进后出原则,由系统负责管理栈内存。而引用类型会被放入堆中,需要我们自己手动管理内存或通过ARC管理。
2.对象的生命周期
在对象的创建和初始化之后,只要对象的retainCount的值比0大,那么它就会一直存在在内存中。通过向一个对象发送retain消息,或者进行copy操作。其他的对象可以引用并持有该对象的所有权。同时,移除引用的时候要发送release消息。
3.对象是如何初始化的
整个对象的初始化过程其实只是为一个分配内存空间,并且初始化 isa_t 结构体的过程
alloc的实现:
直接调用了另一个私有方法 id _objc_rootAlloc(Class cls)。id _objc_rootAlloc(Class cls)调用了callAlloc。
init方法:
init 方法只是调用了 _objc_rootInit 并返回了当前对象。
4.一个objc对象的isa指针指向什么?有什么作用
对象的isa指向类,类的isa指向元类(meta class),元类isa指向元类的根类。
isa:是一个Class 类型的指针. 每个实例对象有个isa的指针,他指向对象的类,而Class里也有个isa的指针, 指向meteClass(元类)。元类保存了类方法的列表。当类方法被调用时,先会从本身查找类方法的实现,如果没有,元类会向他父类查找该方法。同时注意的是:元类(meteClass)也是类,它也是对象。元类也有isa指针,它的isa指针最终指向的是一个根元类(root meteClass).根元类的isa指针指向本身,这样形成了一个封闭的内循环。
5.runtime怎么添加属性、方法等
- ivar表示成员变量
- class_addIvar
- class_addMethod
- class_addProperty
- class_addProtocol
- class_replaceProperty
6.runtime 如何实现 weak 属性
weak策略表明该属性定义了一种“非拥有关系” (nonowning relationship)。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign类似;然而在属性所指的对象遭到摧毁时,属性值也会清空(nil out)。
那么runtime如何实现weak变量的自动置nil
runtime对注册的类会进行布局,会将 weak 对象放入一个 hash 表中。用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会调用对象的 dealloc 方法,假设 weak 指向的对象内存地址是a,那么就会以a为key,在这个 weak hash表中搜索,找到所有以a为key的 weak 对象,从而设置为 nil。
weak属性需要在dealloc中置nil么
在ARC环境无论是强指针还是弱指针都无需在 dealloc 设置为 nil , ARC 会自动帮我们处理
即便是编译器不帮我们做这些,weak也不需要在dealloc中置nil
在属性所指的对象遭到摧毁时,属性值也会清空。
7.runtime如何通过selector找到对应的IMP地址?(分别考虑类方法和实例方法)
- 每一个类对象中都一个对象方法列表(对象方法缓存)
- 类方法列表是存放在类对象中isa指针指向的元类对象中(类方法缓存)
- 方法列表中每个方法结构体中记录着方法的名称,方法实现,以及参数类型,其实selector本质就是方法名称,通过这个方法名称就可以在方法列表中找到对应的方法实现.
- 当我们发送一个消息给一个NSObject对象时,这条消息会在对象的类对象方法列表里查找
- 当我们发送一个消息给一个类时,这条消息会在类的Meta Class对象的方法列表里查找
8.使用runtime Associate方法关联的对象,需要在主对象dealloc的时候释放么?
无论在MRC下还是ARC下均不需要,被关联的对象在生命周期内要比对象本身释放的晚很多,它们会在被 NSObject -dealloc 调用的object_dispose()方法中释放。
补充:对象内存销毁时间表,分四个步骤
-
调用 -release :引用计数变为零
对象正在被销毁,生命周期即将结束
不能再有新的 __weak 弱引用,否则将指向 nil
调用 [self dealloc]
-
父类调用 -dealloc
- 继承关系中最直接继承的父类再调用 -dealloc
- 如果是 MRC 代码 则会手动释放实例变量们(iVars)
- 继承关系中每一层的父类 都再调用 -dealloc
-
NSObject 调 -dealloc
- 只做一件事:调用 Objective-C runtime 中object_dispose() 方法
-
调用 object_dispose()
- 为 C++ 的实例变量们(iVars)调用 destructors
- 为 ARC 状态下的 实例变量们(iVars) 调用 -release
- 解除所有使用 runtime Associate方法关联的对象
- 解除所有 __weak 引用
- 调用 free()
9._objc_msgForward函数是做什么的?直接调用它将会发生什么?
_objc_msgForward是IMP类型,用于消息转发的:当向一个对象发送一条消息,但它并没有实现的时候,_objc_msgForward会尝试做消息转发。直接调用_objc_msgForward是非常危险的事,这是把双刃刀,如果用不好会直接导致程序Crash,但是如果用得好,能做很多非常酷的事。
JSPatch就是直接调用_objc_msgForward来实现其核心功能的。
10.能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?
- 不能向编译后得到的类中增加实例变量
- 能向运行时创建的类中添加实例变量
分析:
- 因为编译后的类已经注册在runtime中,类结构体中的objc_ivar_list 实例变量的链表和instance_size实例变量的内存大小已经确定,同时runtime 会调用class_setIvarLayout 或 class_setWeakIvarLayout来处理strong weak引用,所以不能向存在的类中添加实例变量。
- 运行时创建的类是可以添加实例变量,调用 class_addIvar函数,但是得在调用objc_allocateClassPair之后,objc_registerClassPair之前,原因同上。
11.简述下Objective-C中调用方法的过程(runtime)
-
Objective-C是动态语言,每个方法在运行时会被动态转为消息发送,即:objc_msgSend(receiver, selector),整个过程介绍如下:
- objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类
- 然后在该类中的方法列表以及其父类方法列表中寻找方法运行
- 如果,在最顶层的父类(一般也就NSObject)中依然找不到相应的方法时,程序在运行时会挂掉并抛出异常unrecognized selector sent to XXX
- 但是在这之前,objc的运行时会给出三次拯救程序崩溃的机会,这三次拯救程序奔溃的说明见问题《什么时候会报unrecognized selector的异常》中的说明
补充说明:Runtime 铸就了Objective-C 是动态语言的特性,使得C语言具备了面向对象的特性,在程序运行期创建,检查,修改类、对象及其对应的方法,这些操作都可以使用runtime中的对应方法实现。
12.什么是method swizzling(俗称黑魔法)
简单说就是进行方法交换
在Objective-C中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用Objective-C的动态特性,可以实现在运行时偷换selector对应的方法实现,达到给方法挂钩的目的
每个类都有一个方法列表,存放着方法的名字和方法实现的映射关系,selector的本质其实就是方法名,IMP有点类似函数指针,指向具体的Method实现,通过selector就可以找到对应的IMP
-
交换方法的几种实现方式
- 利用 method_exchangeImplementations 交换两个方法的实现
- 利用 class_replaceMethod 替换方法的实现
- 利用 method_setImplementation 来直接设置某个方法的IMP
13.对象如何找到对应的方法去调用
- 根据对象的isa去对应的类查找方法,isa:判断去哪个类查找对应的方法 指向方法调用的类
- 根据传入的方法编号SEL,里面有个哈希列表,在列表中找到对应方法Method(方法名)
- 根据方法名(函数入口)找到函数实现,函数实现在方法区
14.内存的分区
栈区: 局部变量和方法实参
堆区:OC中使用new方法创建的对象,被创建对象的所有成员变量保存在堆区中
-
BSS段(也叫静态区):
- 教科书:未被初始化的全局变量和静态变量
- Xcode8中: 全局变量和静态变量,不管有没有被初始化,都存放在BSS段中
-
常量区(也叫数据段):
- 教科书: 存储已经初始化的全局变量,静态变量,常量
- xcode8: 存储常量
-
代码段: 程序的代码
15.runtime消息转发机制
- 动态解析方法
对象在收到无法解读的消息后, 首先调用其所属类的这个类方法 :
+ (BOOL)resolveInstanceMethod:(SEL)selector
selector : 那个未知的选择子
返回YES则结束消息转发
返回NO则进入备胎
假如尚未实现的方法不是实例方法而是类方法, 则会调用另一个方法resolveClassMethod:
- 备胎
动态方法解析失败, 则调用这个方法
- (id)forwardingTargetForSelector:(SEL)selector
selector : 那个未知的消息
返回一个能响应该未知选择子的备胎对象
- 消息签名
备胎搞不定, 这个方法就准备要被包装成一个NSInvocation对象, 在这里要先返回一个方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
NSMethodSignature : 该selector对应的方法签名
- 完整的消息转发
给接收者最后一次机会把这个方法处理了, 搞不定就直接程序崩溃
- (void)forwardInvocation:(NSInvocation *)invocation
invocation : 封装了与那条尚未处理的消息相关的所有细节的对象
在这里能做的比较现实的事就是 : 在触发消息前, 先以某种方式改变消息内容, 比如追加另外一个参数, 或是改变消息等等. 实现此方法时, 如果发现某调用操作不应该由本类处理, 可以调用超类的同名方法. 则继承体系中的每个类都有机会处理该请求, 直到NSObject. 如果NSObject搞不定, 则还会调用doesNotRecognizeSelector:来抛出异常, 此时你就会在控制台看到那熟悉的unrecognized selector sent to instance..
16.iOS中__block 关键字的底层实现原理
Block不允许修改外部变量的值,这里所说的外部变量的值,指的是栈中指针的内存地址。__block 所起到的作用就是只要观察到该变量被 block 所持有,就将“外部变量”在栈中的内存地址放到了堆中。进而在block内部也可以修改外部变量的值。
- 那么如何证明“block内部”打印的是堆地址?
把三个16进制的内存地址转成10进制就是:
定义后前:6171559672
block内部:5732708296
定义后后:5732708296
中间相差438851376个字节,也就是 418.5M 的空间,因为堆地址要小于栈地址,又因为iOS中一个进程的栈区内存只有1M,Mac也只有8M,显然a已经是在堆区了。
这也证实了:a 在定义前是栈区,但只要进入了 block 区域,就变成了堆区。这才是 __block 关键字的真正作用。
17.HTTP协议的8种请求类型
- OPTIONS:返回服务器针对特定资源所支持的HTTP请求方法。也可以利用向Web服务器发送'*'的请求来测试服务器的功能性。
- HEAD:向服务器索要与GET请求相一致的响应,只不过响应体将不会被返回。这一方法可以在不必传输整个响应内容的情况下,就可以获取包含在响应消息头中的元信息。
- GET:向特定的资源发出请求。
- POST:向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的创建和/或已有资源的修改。
- PUT:向指定资源位置上传其最新内容。
- DELETE:请求服务器删除Request-URI所标识的资源。
- TRACE:回显服务器收到的请求,主要用于测试或诊断。
- CONNECT:HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
18.列举几种容易造成tableview卡顿的原因,并简单描述解决办法
-
cell中控件的数量太多
- 尽量让cell的布局大致相同,不用风格的cell可以使用不同的重用标识符
-
使用clearColor,透明度,图片圆角
- 渲染耗时比较长,图片圆角可以用xxxx
-
无用cell加载
- 只加载范围内的cell
-
频繁计算高度
- 缓存行高
-
图片没异步加载
- 异步加载图片
-
经常add和remove
- 最好在初始化时就添加完
19.时间复杂度
- 快速排序:O(nlogn)
- 归并排序:O(nlogn)
- 冒泡排序:O(n2)
20.iOS开发中数据持久化有哪几种
- 偏好设置
- plist文件
- 数据库
- CoreData
- 归档解档
21.继承之后的类,子类里面[self class]、[self superClass]、[super class]
- 注:super:编译修饰符,不是指针,只是指向父类标志。本质还是拿到当前对象去调用父类的方法。super并不是拿到父类对象去调用父类方法
- 子类
- 父类
- 子类
22.通知是否可以在子线程中执行
苹果采取通知中心在同一线程中post和转发同一消息策略。我们可以再多线程环境下使用同一个NSNOtificationCenter对象而不需要加锁。但是delloc方法和-postNotificationName:方法不在同一线程中运行时,会出现BAD_ACCESS。例如:sleep(1).
23.串行、并发和同步、异步
串行:每次只有一个任务被执行
并发:同一时间可以有多个任务被执行
同步:只有在完成了塔预定的任务之后才返回
异步:立即返回
24.线程间通信
NSThread:performselector
GCD:dispatch_async(dispatch_get_main_queue(),^{
})
NSOperation:[NSOperaionQueue mainqueue]
25.实现线程同步的方法
串行队列,分组,信号量
26.如何使用队列来避免资源抢夺
线程锁,也可以使用串行队列
27.NSCache优于NSDictionary的几点
- nscache可以自动释放内存
- nscache线程安全
- 一个缓存对象不会拷贝key对象
28.block底层实现
block其实是一个指针结构体
几个重要的结构体:
__block_impl:这是一个结构体,可以理解为block的基类
__main_block_impl_0:可以理解为block变量
__main_block_func_0:可以理解为匿名函数
__main_block_desc_0:block的描述
29.objc在向一个对象发送消息时,发生了什么
根据对象的isa指针找到对象的method_lists列表,如果没有找到,再沿着父类找method_lists,最终找到,确认IMP,发送消息
30.runtime如何实现weak变量的自动置nil
runtime对注册的类会进行布局,对于weak对象会放到一个hash表中,用weak指向的对象内存地址作为key,当此对象的引用计数为0的还是会调用dealloc,从而置nil
31.给类添加一个属性后,在类结构体里哪些元素会发生变化
intance_size:实例内存大小
objc_ivar_list *ivars:属性列表
32.runloop是来做什么的?runloop和线程有什么关系?主线程默认开启了runloop么?子线程呢?
runloop:循环跑圈,用来处理线程里面的事情和消息
runloop和线程的关系:每个线程如果想继续运行,不被释放,就必须有一个runloop来不停的跑圈,来处理线程里面的各个事件和消息
主线程默认开启了一个runloop,子线程默认没有开始runloop
33.苹果是如何实现Autuorelease Pool的
Autorelease Pool:缓存池。可以延迟release,将创建的对象,添加到最近的autoreleasepool中,等到autoreleasePool作用域结束的时候,会将里面所有的对象引用计数-1
34.isa指针
对象的isa指针指向所属类
类的isa指针指向元类
元类额isa指针指向了根类
35.类方法和实例方法的区别
调用方式:类方法必须使用类调用,不可以使用属性,方法存储在元类结构体的method_lists。实例方法使用实例对象调用,可以在实例方法里面使用属性,存储在类的method_lists
36.objc向一个nil对象发送消息会发生什么
- 如果一个方法返回值是一个对象,那么发送给nil消息将返回0(nil)
- 如果方法返回值为指针类型,其指针大小为小于或者等于sizeof(void*),float,double,long double或者long long的整型标量,发送给nil的消息将返回0
- 如果返回值为结构体,发送nil的消息将返回0,结构体中各个字段的值将都是0
- 不是上述几种情况,返回值将是未定义
37.frame和bounds有什么不同
frame指的是:该view在父view坐标系统中的位置和大小
bounds指的是:该view在本坐标系中的位置和大小
38.oc可以多继承么?可以实现多个接口么?category是什么?重写一个类的继承方式好还是分类好。为什么
oc不可以多继承;可以实现多个接口,category是类别;用分类号,仅对本category有效,不会影响到其他类和原有的类的关系;
39.@property的本质是什么?ivar、getter、setter是如何生成并添加到这个类中的
@property:ivar + setter + getter
属性的两大概念:ivar(实例变量) + getter、setter(存取方法)
属性作为oc的一项特性,主要的作用就是在于封装对象中的数据,oc对象通常会把其所需要的数据保存为各种实例变量,实例变量一般通过存取方法来访问,getter读取变量值,setter写入变量值
40.weak和assign区别
assign:可以用非oc对象,基本数据类型。而weak必须用于oc对象
weak:表明该属性定义了一种”非拥有关系“,当属性所指的对象销毁时,属性值会自动清空(nil)
41.@property(nonatomic,retain)NSString *name和@property(nonatomic,copy)NSString *name的setter
``
- (void)setName:(NSString *)name{
- if(_name != name){
[_name release];
_name = [name retain];
}
}
``
``- (void)setName:(NSString *)name{
if(\_name != name){
[\_name release];
\_name = [name copy];
}
}``
42.@synthesie和@dynamic分别有什么作用
- @property有两个对应的词,一个是@synthesize(合成实例变量),一个是@dynamic。
- 如果@synthesize和@dynamic都没有写,那么默认的就是@synthesize var = _var
- @synthsize:如果没有手动实现setter和getter,那么编译器会自动为你加上这两个方法
- @dynamic:属性setter和getter方法由用户自己实现,不自动生成。
43.id声明的对象有什么特性
id声明的对象具有运行时特征,可以指向任意类型的oc对象。
44.category(类别)、extension(扩展)和继承的区别
- 分类有名字,类扩展没有分类名字
- 分类只能扩展方法,类扩展可以扩展属性,成员变量和方法
- 继承可以增加,修改或者删除方法,并且可以增加属性
45.我们说的oc是动态运行时语言是什么意思
主要是将数据类型的确定由编译时,推迟到了运行时。简单来说,运行时机制使我们直到运行时才去决定一个对象的类别以及调用该类别对象指定方法。
46.为什么我们常见的delegate属性都用的是weak而不是retain/strong
为了避免delegate两端产生不必要的循环引用
47.什么时候用delgate,什么时候用notification
delegate:1对1反向传值;
notification:只想要把消息发送出去,告知状态变化,并不关心谁想要知道
48.kvc底层实现
当一个对象调用setvalue方法时,方法内部会做一下操作:
- 检查是否存在相应的key的set方法,如果存在,就调用set方法
- 如果set方法不存在,就会查找与key相同名称并且带有下划线的成员变量,如果有,则直接给成员变量属性赋值
- 如果没有找到_key,就会查找相同名称的属性key,如果有就直接赋值
- 如果还没找到,则调用valueForUndefineKey:和setValue:forUndefineKey:方法。这些方法的默认实现是抛出异常,我们可以根据需要重写它们
49.kvo底层实现
-
KVO
是基于runtime
机制实现的 - 当某个类的属性对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的
setter
方法。派生类在被重写的setter
方法内实现真正的通知机制 - 如果原类为
Person
,那么生成的派生类名为NSKVONotifying_Person
- 每个类对象中都有一个
isa
指针指向当前类,当一个类对象的第一次被观察,那么系统会偷偷将isa
指针指向动态生成的派生类,从而在给被监控属性赋值时执行的是派生类的setter
方法 - 键值观察通知依赖于
NSObject
的两个方法:willChangeValueForKey:
和didChangevlueForKey:
;在一个被观察属性发生改变之前,willChangeValueForKey:
一定会被调用,这就 会记录旧的值。而当改变发生后,didChangeValueForKey:
会被调用,继而observeValueForKey:ofObject:change:context:
也会被调用。
50.ViewController生命周期
- initWithCoder:通过nib文件初始化时触发
- awakeFromNib:nib文件被加载的时候
- loadView:开始加载视图控制器自带的view
- viewDidLoad:视图控制器的view被加载完成
- viewWillAppear:
- updateViewConstraints:
- viewWillLayoutSubviews:
- viewDidLayoutSubviews
- viewDidAppear
- viewWillDisappear
- viewDidDisappear
51.方法和选择器有何不同
selector是一个方法的名字,方法是名字和实现的组合体
52.你是否接触过OC中的反射机制
-
class反射
-
通过类名的字符串形式实例化对象:
Class class = NSClassFromString(@"Student");
Student *stu = [[class alloc] init];
-
将类名变为字符串
Class class = [Student class];
NSString *className = NSStringFromClass(class);
-
-
SEL的反射
- 通过方法的字符串形式实例化方法
SEL selector = NSSelectorFromString(@"setName");
[stu performSelector:selector withObject:@"M"];
- 将方法变成字符串
NSStringFromSelector(@selector(setName:));
- 通过方法的字符串形式实例化方法
53.类变量的@public,@protected,@private,@package声明各有什么含义
@public:任何地方都能访问
@protected:该类和子类中访问,是默认的
@private:只能在本类中访问
@package:本包内使用,跨包不可以
54.什么是谓词
谓词是通过NSPredicate
给定的逻辑条件作为约束条件,完成对数据的筛选
定义谓词对象,谓词对象中包含了了过滤条件
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age<%d",30];
使用谓词条件过滤数组中的元素,过滤之后返回查询结果
NSArray *array = [persons filteredArrayUsingPredicate:predicate];
55.如何访问并修改一个类的私有属性
- kvc获取
- 通过runtime访问并修改
56.isKindofClass、isMemberClass、selector作用分别是什么
- isKindofClass:某个对象属于某个类型或者继承自某类型
- isMemberClass:某个对象确切属于某个类型
- selector:通过方法名,获取内存中的函数的入口地址
57.常用的instruments
- Time Profiler:性能分析
- Zombies:检查是否访问了僵尸对象
- Allocations:检查内存
- Leaks:检查内存。看是否有内存泄漏
58.runtime实现的机制是什么,怎么用,一般用于干嘛
- runtime是运行时机制,是一套C语言库
- 我们编写的OC代码,最终都会转成runtime的东西
- runtime是OC的底层实现
59.cocoapods原理
执行pod命令之后,项目中会生成:ProjectName.xcworkspace、Podfile.lock、Pods等文件;
CocoaPods的工作主要是通过ProjectName.xcworkspace来组织的,在打开ProjectName.xcworkspace文件后,发现Xcode会多出一个Pods工程。
- 库文件引入及配置:
库文件的引入主要由Pods工程中的Pods-ProjectName-frameworks.sh脚本负责,在每次编译的时候,该脚本会帮你把预引入的所有三方库文件打包的成ProjectName.a静态库文件,放在我们原Xcode工程中Framework文件夹下,供工程使用。
如果Podfile使用了use_frameworks!,这是生成的是.framework的动态库文件。引入方式也略有不同。
- Resource文件:
Resource资源文件主要由Pods工程中的Pods-ProjectName-resources.sh脚本负责,在每次编译的时候,该脚本会帮你将所有三方库的Resource文件copy到目标目录中。
- 依赖参数设置:
在Pods工程中的的每个库文件都有一个相应的SDKName.xcconfig,在编译时,CocoaPods就是通过这些文件来设置所有的依赖参数的,编译后,在主工程的Pods文件夹下会生成两个配置文件,Pods-ProjectName.debug.xcconfig、Pods-ProjectName.release.xcconfig。
- 波浪线**~ > ** 含义:从指定版本到倒数第二位版本号升1为止,比如 ‘~> 0.3.7’所指的版本区间为[0.3.7, 0.4.0),即>=版本0.3.7,<版本0.4.0
- 在使用import引入文件时,不能自动补齐
Target -> Build Settings ,User Header Search Paths条目中,添加${SRCROOT}或者$(PODS_ROOT),并且选择Recursive,递归搜索,然后就可以自动补齐了。
60.framework制作
1.新建工程,选择
Framework & Library
->Cocoa Touch Framework
2.创建功能类
3.实现功能类
-
4.更改参数:
Build Settings
-
Dead Code Stripping
: NO -
Link With Standard Libraries
: NO -
Mach-O Type
: 增加armv7s
并设置为Static Library
-
5.设置Headers:
Build Phases
的Headers
将公共的拖到Public下
- 6.在一开始生成的.h中导入头文件
#import <xxx/xxx.h>
- 7.编译并合并
61.objc_msgForward函数是做什么的,直接调用它将会发生什么
objc_msgForward是IMP类型,用于消息转发。当对一个对象发送一条消息,但是它并没有实现的时候,就会调用这个方法
62.什么是TCP/UDP
TCP:传输控制协议
UDP:用户数据协议
TCP是面向连接的,建立连接需要经历三次握手,是可靠的传输层协议
UDP是面向无连接的,数据传输是不可靠的,它只管发,不管是否能收到
- 三次握手协议
首先Client端发送连接请求报文,Server段接受连接后回复ACK报文,并为这次连接分配资源。Client端接收到ACK报文后也向Server段发生ACK报文,并分配资源,这样TCP连接就建立了。
- 四次握手协议
假设Client端发起中断连接请求,也就是发送FIN报文。Server端接到FIN报文后,意思是说我Client端没有数据要发给你了
,但是如果你还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。所以你先发送ACK,告诉Client端,你的请求我收到了,但是我还没准备好,请继续你等我的消息
。这个时候Client端就进入FIN_WAIT
状态,继续等待Server端的FIN报文。当Server端确定数据已发送完成,则向Client端发送FIN报文,告诉Client端,好了,我这边数据发完了,准备好关闭连接了
。Client端收到FIN报文后,就知道可以关闭连接了,但是他还是不相信网络,怕Server端不知道要关闭,所以发送ACK后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。
,Server端收到ACK后,就知道可以断开连接了
。Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可以关闭连接了。Ok,TCP连接就这样关闭了!
63.通信底层实现原理(OSI七层模型)
物理层、数据链路层、网络层、传输层、回话层、表示层、应用层
64.XMPP
xmpp是一种以XML为基础的开放式实时通信协议
65.OC中创建线程的方法是什么?如果在主线程中执行代码,方法是什么
// 创建线程的方法
- [NSThread detachNewThreadSelector:nil toTarget:nil withObject:nil]
- [self performSelectorInBackground:nil withObject:nil];
- [[NSThread alloc] initWithTarget:nil selector:nil object:nil];
- dispatch_async(dispatch_get_global_queue(0, 0), ^{});
- [[NSOperationQueue new] addOperation:nil];
// 主线程中执行代码的方法
- [self performSelectorOnMainThread:nil withObject:nil waitUntilDone:YES];
- dispatch_async(dispatch_get_main_queue(), ^{});
- [[NSOperationQueue mainQueue] addOperation:nil];
66.tableview的重用机制
UITableView通过重用单元格来达到节省内存的目的:通过为每个单元格指定一个重用标识符,即指定了单元格的种类,当屏幕上的单元格滑出屏幕时,系统会把这个单元格添加到重用队列,等待被重用。当有新单元格从屏幕外滑入屏幕内时,从重用队列中找看有没有可以重用的单元格,如果有,就拿过来用,如果没有就创建一个来使用
67.用伪代码写一个线程安全的单例模式
static id _instance
+ (instancetype)shareData{
return [[self alloc] init];
}
+ (instancetype)alloceWithZone:(struct _NSZone *)zone{
static dispatch_once_t oncetoken;
dispatch_once(&oncetoken,^{
\_instace = [super allocWithZone: zone];
});
return \_instance;
}
- (id)copyWithZone:(NSZone *)zone{
return _instance;
}
68.HTTP协议中POST和GET方法有哪些区别
- GET用于向服务器请求数据,POST用于提交数据
- GET请求,请求参数拼接形式暴露在地址栏,而POST请求参数则放在请求体里面。
- GET请求的URL有长度限制,POST请求不会有长度限制
69.请简单介绍下APNS发送系统消息的机制
- 1.应用在通知中心注册,由iOS系统向APNS请求返回设备令牌(device token)
- 2.应用程序接收到设备令牌并发送给自己的服务器
- 3.服务器把要推送的内容和设备发送给APNS
- 4.APNS根据设备令牌找到设备,再有iOS根据APPID把推送内容展示
70.不用中间变量,用两种方法交换A和B的值
中间变量
void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
}
加法
void swap(int a, int b) {
a = a + b;
b = a - b;
a = a - b;
}
异或
void swap(int a, int b) {
a = a ^ b;
b = a ^ b;
a = a ^ b;
}
71.实现响应链传递的两种方式?在那些地方使用过
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
``
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;``
使用:
- 扩大view的点击区域
- 将事件传递给兄弟view
72.block底层实现
能够捕获它所在函数内部的变量
的函数指针、匿名函数或者闭包
void (*test)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
那么这整句就是说
定义一个函数指针指向一个新创建的__main_block_impl_0实例的地址。注意创建这个实例时构选函数传的两个参数, 正是编译器帮我们生成的静态函数__main_block_func_0及__main_block_desc_0的变量__main_block_desc_0_DATA
73.armv7,armv7s,arm64,i386,x86_64
iOS测试分为模拟器测试和真机测试,处理器分为32位处理器,和64位处理器。
模拟器32位处理器测试需要i386架构。(iphone5及其以下)
模拟器64位处理器测试需要x86_63架构。(iphone5s及其以上)
真机32位处理器需要armv7或者armv7s构架。(iphone4用armv7。iphone5,iphone5s用armv7s)
真机64位处理器需要arm64架构。(iphone6以上)
74.Xcode中将图片放入images.xcassets和直接拖入的区别
-
将图片放如images.xcassets
- 在mainBundle里面xcode会生成一个assets.car文件,将images>xcassets的图片打包在这里。
- sb不需要加后缀名
- 不能通过imagesWithContentsOfFile来加载
-
将图片直接拖入
- 直接放入了mainBundle里面
- sb需要加上后缀
- imageName,.png可以不需要使用后缀;.jpg需要加后缀
75.iOS获取设备的唯一标识的方法总结
76.tcp/ip协议层次结构
五层:应用层、传输层、网络层、数据链路层、物理层
五层:应用层、传输层、网络层、网络接口层
77.响应链
能够相应时间的对象都是UIResponder的子类对象。
系统通过不断查找nextResponder来相应点击事件,而所有可交互的空间都是UIResponder直接或者间接的子类。
-
响应者
- aView
- UIView
- ViewController
- UIApplication
- Appdelegate
-
重要的方法
-
返回相应点击时间的对象
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;
-
根据点击坐标返回时间是否发生在本视图以内
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;
-
-
使用
- 不规则图形的点击事件
- 扩大或者缩小点击范围