1 weak和assign的区别?以及什么时候使用weak 与assign:
Weak- 只修饰对象,
Assign-可修饰对象跟基本数据类型,修饰对象时易造成野指针问题。
在ARC 中,有可能出现循环引用,往往通过一端使用weak 来解决。比如代理属性
2 值类型与引用类型的区别?
在swift中分两类:每个值类型的实例都拥有各自唯一的数据。通常是结构体,元祖,枚举
引用类型, 引用类型的实例共享它们的数据, 通常是一个类(类的属性)
值类型最基本的特征就是进行值拷贝。在OC中想要达到这样的效果,需要使用copy。
在OC中NSString, NSArray, NSDictionry就是引用类型;
在Swift中 Array、String和Dictionary都是值类型;
3 oc 中protocol 与category 中使用@property的作用
在protocol中添加property时,其实就是添加了 getter 和 setter 方法,在实现这个protocol的类中,我们要自己手动添加实例变量
例: @synthesize name = _name; 此行代码即添加了实例变量及实现了protocol中属性的getter、setter方法
在category中添加property时, 在@implentation添加 getter 和 setter方法时, 由于category不能添加实例变量,故必须通过运行时添加associated object的方法来添加实例变量
objc_getAssociatedObject/objc_setAssociatedObject
4 EXE_BAD_ACCESS出现这种错误时什么原因?如何解决?
EXE_BAD_ACCESS 一般是内存问题引起的,引用了已经释放的对象导致的。
解决方法:Xcode-Edit Scheme-Run-Diagnostic-选中Enable Zombie Objects
5 ARC的理解
就是代码中自动加入了retain/release,原先需要手动添加的用来处理内存管理的引用计数的代码可以自动地由编译器完成了。编译器自动生成实例的引用计数器管理部分代码。
6 GCD NSOperation
进程:一个运行中的程序
线程:程序执行中的最小单元,线程是进程中的一个实体
同步:只能在当前线程按先后顺序执行。不开启新线程
异步:可以在当前线程开启多个新线程执行,可不按顺序执行。
队列:装载线程任务的队形结构
并发:线程同时一起执行
串行:线程执行只能依次逐一按先后顺序执行。
1. NSThread
每个NSThread对象对应一个线程,真正最原始的线程。
1)优点:NSThread 轻量级最低,相对简单。
2)缺点:手动管理所有的线程活动,如生命周期、线程同步、睡眠等
2. NSOperation
自带线程管理的抽象类。
1)优点:自带线程周期管理,操作上可更注重自己逻辑。还能设置操作队列中的个数
2)缺点:面向对象的抽象类,只能实现它或者使用它定义好的两个子类:NSInvocationOperation 和 NSBlockOperation。
7 数组与链表的区别
数组:将元素存在内存中,由于元素占用的内存相同,可以通过下标快速的访问数组中的任何元素。如果想在数组中增加一个元素,需要大量的移动元素,在内存中空出一个元素的空间,然后将要增加的元素放在其中。
链表:链表中的元素是不按顺序存储的。而是通过元素中的指针联系到一起的。如果想在数组中增加一个元素,只要移动链表元素中的指针就可以了。
a, 数组必须事先定义固定的长度(元素个数),不能适应数据动态地增减的情况。当数据增加时,可能超出原先定义的元素个数;当数据减少时,造成内存浪费。
b,链表动态地进行存储分配,可以适应数据动态地增减的情况,且可以方便地插入、删除数据项。(数组中插入、删除数据项时,需要移动其它数据项)
数组从栈中分配空间,由操作系统分配资源,释放资源
链表从堆中分配空间,申请比较麻烦。
8 Block 原理/用copy修饰的原因; __weak 和 __block 的区别
Block创建的时候是分配在栈上的,出了作用域就会销毁。使用copy可以把它复制到堆上。
Block的功能是保存代码片段和代码区域内的函数变量, 预先准备好代码, 并在需要的时候执行。
__block不但可以在ARC环境下面使用还可以在MRC环境下面使用,并且可以修饰对象和基本数据类型.
__weak只能在ARC 环境下面使用,并且只能修饰对象,不能修饰基础数据类型。
__block 修饰的对象可以在block 内部进行修改,而__weak 修饰 对象不能呗修改。
9 http 请求报文/响应报文的结构
HTTP请求由三部分组成,分别是:请求行、消息报头、请求正文。
HTTP响应也由三个部分组成,分别是:状态行、消息报头、响应正文。
10: 关于runtime 的学习
Runtime :运行时语言,底层是纯c的一个api。
Runtime 可以为分类增加属性,替换系统的api,为系统增加方法等
1.为分类增加属性。
重新set/get方法(记住:声明属性的时候如果是readlyonly 不想呗外面修改的话,只需要实现get 方法)
set方法:objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)get方法:objc_getAssociatedObject(id object, const void *key)
set方法 const void *key 与 get方法const void *key 保持一致就OK了。
2.预防字典key 为空或者nil 时的报错。
SEL selector 的简写,俗称方法选择器,实质存储的是方法的名称
IMP 是方法的实现,是一个函数指针
1-获取类中的类方法(+号方法)
class_getClassMethod(class, origSelector);
2-获取类中的对象方法(-号方法)
class_getInstanceMethod(class, origSelector);
3-获取类中方法的实现
class_getMethodImplementation(class, origSelector);
4-获取类中某个方法的实现,如子类没有方法会返回父类的方法
class_getMethodImplementation(class, origSelector);
5-获取某个类中某个方法的实现
method_getImplementation(swizzleMethod);
6 获取方法的Type字符串(包含参数类型和返回值类型)
const char *types = method_getTypeEncoding(swizzleMethod);
为类增加方法,如果该类中方法已经存在,返回no
BOOL isAdd = class_addMethod(class, origSelector,class_getMethodImplementation(class, newSelector), method_getTypeEncoding(swizzleMethod));
if (isAdd) {
class_replaceMethod(class, newSelector,method_getImplementation(orignalMethod), method_getTypeEncoding(orignalMethod));
}else{
method_exchangeImplementations(orignalMethod, swizzleMethod);
}
3 动态的增加方法
performSelector调用未实现的方法,必须与resolveInstanceMethod结合使用
_cmd 表示方法的编号,打印结果为当前执行的方法名
type: 方法类型:void用v来表示,id参数用@来表示,SEL用:来表示
+ (BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == NSSelectorFromString(@"love")) {
class_addMethod(self, sel, (IMP)haha , "v@:@");
}
return [super resolveInstanceMethod:sel];
}
被调用的方法实现部分未找到,而消息转发机制启动之前的时刻调用
void haha(id sexlf, SEL _cmd, NSNumber *meter) {
NSLog(@"测试%@", meter);
}
4 获取类中指定的属性+属性列表
获取属性的列表:@param outCount On return
class_copyPropertyList(<#__unsafe_unretained Class cls#>, <#unsigned int *outCount#>)
5 runtime 如何实现 weak 属性
Weak 这种属性就不保留新值,也不释放旧值。然而在属性遭到摧毁时,也会清空
那么runtime如何实现weak变量的自动置nil?
Runtime 会对注册的类,进行布局,会将weak对象放入一个hash 表中,以weak 指向的对象的地址作为key,当此对象的引用计数器为0的时候,会调用对象的delloc方法,假设weak 对象指向的内存地址是a,那么就会以a为key。在这个weak hash表中搜索,找到所有以a为key的对象。从而置为nil。ARC中无论weak 还是strong 都无需在delloc 中设置为nil
6. runtime如何通过selector找到对应的IMP地址?(分别考虑类方法和实例方法)
IMP:函数指针指向了这个方法的具体实现,IMP指向的方法函数与objc_msgSend函数类型相同。参数都包含id 与sel。每个sel 都对应一个方法选择器,每个实例对象中的sel 肯定是唯一的。通过一组id 与 sel 参数就能确定一个方法的实现地址。
当我们发送一个消息给Nsobject 对象时,这条消息会在对象的类对象方法列表中查找。
当我们发送一个消息给一个类时,这条消息会在类的meta Class对象的方法列表中查找。
每个类对象中都有一个类对象方法列表。方法中记录方法名称,方法实现,以及参数类型。selctor 本质就是方法的名称,通过这个方法名称就可以在方法列表找到对应的方法实现。
7._objc_msgForward 函数是做什么的?直接调用他将会发生什么?
_objc_msgForward 是IMP类型,用于消息转发,当向一个对象发送一条消息,但是他 并没有实现的时候,_objc_mgsForward 会尝试做消息转发,直接调用_objc_msgForward 非常危险。如果用不好会导致Crash。用的话就非常酷,JSPatch就是直接调用_objc_msgForward 来实现核心功能的。
8. 能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?
。不能向编译后的类中增加实例变量;但是可以向运行时创建的类中添加实例变量
分析如下:编译后的类已经注册在runtime中,类结构体中的objc _ivar_list 实例变量的链表和instance_size 实例变量的内存大小已经确定,同时runtime会调用class_setIvarLayout 或者 class_setWeakIvarLayout来处理strong weak引用,所以不能向存在的类中添加实例变量
。运行时创建的类可以添加实例变量,调用class_addIvar 函数,但是得在调用objc_allocateClassPair之后,objc_registerClassPair之前,原因同上。
9. 黑魔法?简单的说就是方法交换
Oc 调用一个方法,其实就是向一个对象发送消息,查找消息的唯一依据是selestor的名字,利用oc的动态特性,可以实现在运行时偷换selector对应的方法实现。每个类都有一个方法列表,存放着方法的名字和方法实现的映射关系。selector 本质其实就是方法名。IMP有点类似函数指针。指向具体method实现。通过selector就可以找到对应的IMP
交换方法的几种实现方式
- 利用 method_exchangeImplementations 交换两个方法的实现
- 利用 class_replaceMethod 替换方法的实现
- 利用 method_setImplementation 来直接设置某个方法的IMP
面试题优化
1.适应bit-64位如:
- int -> NSInteger
- unsigned -> NSUInteger
- float -> CGFloat
- 动画时间 -> NSTimeInterval
2.使用新版的枚举
例如:typedef NS_ENUM(NSInteger, UIViewAnimationCurve) {
UIViewAnimationCurveEaseInOut,
UIViewAnimationCurveEaseIn,
UIViewAnimationCurveEaseOut,
UIViewAnimationCurveLinear
};
3. 怎么用 copy 关键字?
1.NSString、NSArray、NSDictionary 等等经常使用copy关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary;
2 Block 在MRC中block的方法内部实现是在栈区,使用copy可以把它放在堆区。在ARC中使用对block使用copy与strong 效果一样。写不写都可以,但是如果不写 copy ,该类的调用者有可能会忘记或者根本不知道“编译器会自动对 block 进行了 copy 操作”
4.@property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的
property = ivar +存储方法(setter+getter)
Property 主要作用就在于封装对象中的数据,oc通常会把所需要的数据保存为各种实例变量。实例变量一般通过存储方法来访问。其中获取方法(getter)用于读取变量值,设置方法setter 用于写入变量的值。编译器会自动写出一套存取方法,用以访问给定类型中具有给定名称的变量。
5. 用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?
1父类的指针可以指向子类的对象。使用copy的原因是为了保证本对象不受外界的干扰,使用copy无论给我传入的是一个可变或者不可变得对象,对象本身持有的是一个不可变
- 1、美团面试题:有没有使用过performSelector,使用,什么时候使用?动态添加方法的时候使用? 为什么动态添加方法 // 默认OC方法都有两个默认存在的隐式参数,self(哪个类的方法),_cmd(方法编号)
- void run(id self, SEL _cmd, NSNumber *metre) {
- NSLog(@"跑了%@",metre);
- }
- 2、什么时候调用:只要调用没有实现的方法 就会调用方法去解决,这里可以拿到那个未实现的方法名 // 作用:去解决没有实现方法,动态添加方法
- +(BOOL)resolveInstanceMethod:(SEL)sel{
- class:给谁添加方法
- SEL:添加哪个方法
- IMP:方法实现,函数入口,函数名,如:(IMP)run,方法名run强转成IMP
- type:方法类型,通过查苹果官方文档,V:void,
- class_addMethod(<#__unsafe_unretained Class cls#>, <#SEL name#>, <#IMP imp#>, <#const char *types#>)
- // [NSStringFromSelector(sel) isEqualToString:@"eat"];
- if (sel == @selector(run:)) {
- // 添加方法
- class_addMethod(self, sel, (IMP)run,"v@:");
- return YES;
- }
- return [super resolveInstanceMethod:sel];
- }