前言
在2019年iOS面试题中看到的一些面试题,自己搜索答案将其整理放到一起。
基础
1.我们说的Objective-C是动态运行时语言是什么意思
- object-c类的类型和数据变量的类型都是在运行是确定的,而不是在编译时确定。例如:多态特性,我们可以使用父类对象来指向子类对象,并且可以用来调用子类的方法。运行时(runtime)特性,我们可以动态的添加方法,或者替换方法。
2.讲一下MVC和MVVM,MVP?
- MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,Model 呈现数据,View 呈现用户界面,而 View Controller 调节它两者之间的交互。
- MVVM全名是Model View View-Model。
ViewModel将表示逻辑从 Controller 移出放到一个新的对象里,即 View Model。就是那些将 Model 数据转换为 View 可以呈现的东西的事情,例如将一个 NSDate 转换为一个格式化过的 NSString。 - MVP全名是Model-view-presenter,是使用者界面设计模式的一种。
Model 定义使用者界面所需要被显示的数据模型,一个模型包含着相关的业务逻辑。View 视图为呈现使用者界面的终端,用以表现来自 Model 的数据,和使用者命令路由再经过 Presenter 对事件处理后的数据。
3.为什么代理要用weak?代理的delegate和dataSource有什么区别?block和代理的区别?
- 防止循环引用。dataSource负责数据,delegate负责调用方法。
- block本质上是一个OC对象,因为它的内部也有isa指针,代理是一种设计模式
- 1.从源头上理解和区别block和delegate
delegate运行成本低,block的运行成本高。
block出栈需要将使用的数据从栈内存拷贝到堆内存,当然对象的话就是加计数,使用完或者block置nil后才消除。delegate只是保存了一个对象指针,直接回调,没有额外消耗。就像C的函数指针,只多做了一个查表动作。 - 2.从使用场景区别block和delegate
有多个相关方法。假如每个方法都设置一个 block, 这样会更麻烦。而 delegate 让多个方法分成一组,只需要设置一次,就可以多次回调。当多于 3 个方法时就应该优先采用 delegate。当1,2个回调时,则使用block。
delegate更安全些,比如: 避免循环引用。使用 block 时稍微不注意就形成循环引用,导致对象释放不了。这种循环引用,一旦出现就比较难检查出来。而 delegate 的方法是分离开的,并不会引用上下文,因此会更安全些。
4.如何访问并修改一个类的私有属性
1.KVC(键值编码)
Person *p = [Person new];
//修改私有属性的值
[p setValue:@"yyMae" forKey:@"name"];
//访问私有属性的值
NSString *name = [p valueForKey:@"name"];
2.通过runtime获取或修改一个类私有属性的值
Person *p = [Person new];
// IVar是runtime声明的一个宏
unsigned int count = 0; //count记录变量的数量
// 获取类的所有属性变量
Ivar *members = class_copyIvarList([Person class], &count);
for (int i = 0; i < count; i++) {
Ivar ivar = members[i];
// 取得属性名并转成字符串类型
const char *memberName = ivar_getName(ivar);
NSLog(@"%s",memberName);
}
Ivar name = members[0];
// 修改属性值
object_setIvar(Person, name, @"yyMae");
5.objc使用什么机制管理对象内存?
引用计数,管理方式 1.MRC 2.ARC
block
1.block的本质是什么?一共有几种block?都是什么情况下生成的?
block的一些常用问答
2.使用系统的某些block api,是否考虑引用循环问题?
系统的某些block api中,UIView的block版本写动画时不需要考虑,但也有一些api 需要考虑:所谓“引用循环”是指双向的强引用,所以那些“单向的强引用”(block 强引用 self )没有问题,比如这些:
[UIView animateWithDuration:durationanimations:^{ [self.superviewlayoutIfNeeded]; }];
[[NSOperationQueuemainQueue]addOperationWithBlock:^{ self.someProperty= xyz; }];
[[NSNotificationCenterdefaultCenter]addObserverForName:@"someNotification"object:nilqueue:
[NSOperationQueuemainQueue]usingBlock:^(NSNotification* notification) { self.someProperty= xyz; }];
但如果你使用一些参数中可能含有 ivar 的系统 api ,如 GCD 、NSNotificationCenter就要小心一点:比如GCD 内部如果引用了 self,而且 GCD 的其他参数是 ivar,则要考虑到循环引用:
__weak__typeof__(self) weakSelf = self;
dispatch_group_async(_operationsGroup, _operationsQueue, ^{
__typeof__(self) strongSelf = weakSelf;
[strongSelf doSomething];
[strongSelf doSomethingElse];
} );
检测代码中是否存在循环引用问题,可使用 Facebook 开源的一个检测工具FBRetainCycleDetector。
Runtime
1.runtime如何实现weak属性?如何实现weak变量的自动置nil?
weak 对象会放入一个 hash 表中。
用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会 dealloc。
假如 weak 指向的对象内存地址是addr,那么就会以addr为键, 在这个 weak 表中搜索,找到所有以addr为键的 weak 对象,从而设置为 nil。
2.runtime如何通过selector找到对应的IMP地址?
在寻找IMP的地址时,runtime提供了两种方法
// IMP class_getMethodImplementation(Class cls, SEL name); class_getMethodImplementation(objc_getMetaClass("A"),@selector(methodName));
// IMP method_getImplementation(Method m); class_getMethodImplementation([A class],@selector(methodName));
3.能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?
1.不能向编译后得到的类增加实例变量
2.能向运行时创建的类中添加实例变量
解释:
1.编译后的类已经注册在runtime中,类结构体中的objc_ivar_list实例变量的链表和instance_size实例变量的内存大小已经确定,runtime会调用class_setvarlayout或class_setWeaklvarLayout来处理strong weak引用.所以不能向存在的类中添加实例变量
2.运行时创建的类是可以添加实例变量,调用class_addIvar函数.但是的在调用objc_allocateClassPair之后,objc_registerClassPair之前,原因同上
4.在开发中如何使用runtime?什么应用场景?
Runtime重要知识点以及使用场景
类结构
1.isa指针?
在Objective-C中,任何类的定义都是对象。类和类的实例(对象)没有任何本质上的区别。任何对象都有isa指针
每个实例对象有个isa的指针,他指向对象的类,而Class里也有个isa的指针, 指向meteClass(元类)。元类保存了类方法的列表。当类方法被调用时,先会从本身查找类方法的实现,如果没有,元类会向他父类查找该方法。同时注意的是:元类(meteClass)也是类,它也是对象。元类也有isa指针,它的isa指针最终指向的是一个根元类(root meteClass).根元类的isa指针指向本身,这样形成了一个封闭的内循环。
2.类方法和实例方法有什么区别?
1、在类方法中不能调用实例方法,只能访问和自己一样的类方法,但实例方法可以访问类方法和实例方法。
2、在类方法中不能引用实例变量(用static修饰的变量),但实例方法可以引用成员变量和实例变量。
3、在类方法中不能使用super、this关键字。
4、类方法不能被覆盖,但实例方法可以被覆盖。
5、类方法的调用是:类名.类方法,而实例方法的调用必须new出一个对象,即:对象.实例方法。
3.介绍一下分类,能用分类做什么?内部是如何实现的?它为什么会覆盖掉原来的方法?
主要作用是在不改变原有类的前提下,动态地给这个类添加一些方法。
category内部实现原理
未完待续···