喜欢就关注我呗!
1.设计模式是什么? 你知道哪些设计模式,并简要叙述?
设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类型的事情。
1). MVC模式:Model View Control,把模型 视图 控制器 层进行解耦合编写。
2). MVVM模式:Model View ViewModel 把模型 视图 业务逻辑 层进行解耦和编写。
3). 单例模式:通过static关键词,声明全局变量。在整个进程运行期间只会被赋值一次。
4). 观察者模式:KVO是典型的通知模式,观察某个属性的状态,状态发生变化时通知观察者。
5). 委托模式:代理+协议的组合。实现1对1的反向传值操作。
6). 工厂模式:通过一个类方法,批量的根据已有模板生产对象。
2.#import跟 #include 有什么区别,@class呢
1). #import是Objective-C导入头文件的关键字,#include是C/C++导入头文件的关键字,使用#import头文件会自动只导入一次,不会重复导入。
2). @class告诉编译器某个类的声明,当执行时,才去查看类的实现文件,可以解决头文件的相互包含。
3.Objective-C的类可以多重继承么?可以实现多个接口么?Category是什么?重写一个类的方式用继承好还是分类好?为什么?
答:Objective-C的类不可以多重继承;可以实现多个接口(协议);Category是类别;一般情况用分类好,用Category去重写类的方法,仅对本Category有效,不会影响到其他类与原有类的关系。
4.@property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的
@property 的本质是什么?
@property = ivar + getter + setter;
“属性” (property)有两大概念:ivar(实例变量)、getter+setter(存取方法)
“属性” (property)作为 Objective-C 的一项特性,主要的作用就在于封装对象中的数据。 Objective-C 对象通常会把其所需要的数据保存为各种实例变量。实例变量一般通过“存取方法”(access method)来访问。其中,“获取方法” (getter)用于读取变量值,而“设置方法” (setter)用于写入变量值。
5.什么情况使用 weak 关键字,相比 assign 有什么不同?
1.在 ARC 中,在有可能出现循环引用的时候,往往要通过让其中一端使用 weak 来解决,比如: delegate 代理属性。
2.自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用 weak,自定义 IBOutlet 控件属性一般也使用 weak;当然,也可以使用strong。
IBOutlet连出来的视图属性为什么可以被设置成weak?
因为父控件的subViews数组已经对它有一个强引用。
不同点:
assign 可以用非 OC 对象,而 weak 必须用于 OC 对象。
weak 表明该属性定义了一种“非拥有关系”。在属性所指的对象销毁时,属性值会自动清空(nil)。
6.浅拷贝和深拷贝的区别?
答:
浅拷贝:只复制指向对象的指针,而不复制引用对象本身。
深拷贝:复制引用对象本身。内存中存在了两份独立对象本身,当修改A时,A_copy不变。
NSString *str = @"hello word!";
NSString *strCopy = [str copy] // 指针复制,strCopy与str的地址一样
NSMutableString *strMCopy = [str mutableCopy] // 内容复制,strMCopy与str的地址不一样
7.写一个 setter 方法用于完成 @property (nonatomic, retain) NSString*name,写一个 setter 方法用于完成 @property (nonatomic, copy) NSString *name
答:
// retain
- (void)setName:(NSString *)str {
[str retain];
[_name release];
_name = str;
}
// copy
- (void)setName:(NSString *)str {
id t = [str copy];
[_name release];
_name = t;
}
8.@synthesize 和 @dynamic 分别有什么作用?
@property有两个对应的词,一个是@synthesize(合成实例变量),一个是@dynamic。
如果@synthesize和@dynamic都没有写,那么默认的就是 @synthesize var = _var;
// 在类的实现代码里通过 @synthesize 语法可以来指定实例变量的名字。(@synthesize var = _newVar;)
1. @synthesize 的语义是如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法。
2. @dynamic 告诉编译器,属性的setter与getter方法由用户自己实现,不自动生成(如,@dynamic var)。
9.id 声明的对象有什么特性?
答:id 声明的对象具有运行时的特性,即可以指向任意类型的Objcetive-C的对象。
10.Objective-C 中创建线程的方法是什么?如果在主线程中执行代码,方法是什么?如果想延时执行代码、方法又是什么?
答:线程创建有三种方法:使用NSThread创建、使用GCD的dispatch、使用子类化的NSOperation,然后将其加入NSOperationQueue;在主线程执行代码,方法是performSelectorOnMainThread,如果想延时执行代码可以用performSelector:onThread:withObject:waitUntilDone:
11.我们说的OC是动态运行时语言是什么意思?
答:主要是将数据类型的确定由编译时,推迟到了运行时。简单来说, 运行时机制使我们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法。
12.什么是 KVO 和 KVC?
1). KVC(Key-Value-Coding):键值编码 是一种通过字符串间接访问对象的方式(即给属性赋值)
举例说明:
stu.name = @"张三" // 点语法给属性赋值
[stu setValue:@"张三" forKey:@"name"]; // 通过字符串使用KVC方式给属性赋值
stu1.nameLabel.text = @"张三";
[stu1 setValue:@"张三" forKey:@"nameLabel.text"]; // 跨层赋值
2). KVO(key-Value-Observing):键值观察机制 他提供了观察某一属性变化的方法,极大的简化了代码。
KVO只能被KVC触发,包括使用setValue:forKey:方法和点语法。
// 通过下方方法为属性添加KVO观察
- (void)addObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(nullable void *)context;
// 当被观察的属性发送变化时,会自动触发下方方法
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context{}
KVC 和 KVO 的 keyPath 可以是属性、实例变量、成员变量。
13.KVC的底层实现?
当一个对象调用setValue方法时,方法内部会做以下操作:
1). 检查是否存在相应的key的set方法,如果存在,就调用set方法。
2). 如果set方法不存在,就会查找与key相同名称并且带下划线的成员变量,如果有,则直接给成员变量属性赋值。
3). 如果没有找到_key,就会查找相同名称的属性key,如果有就直接赋值。
4). 如果还没有找到,则调用valueForUndefinedKey:和setValue:forUndefinedKey:方法。
这些方法的默认实现都是抛出异常,我们可以根据需要重写它们。
24.谈谈 UITableView 的优化
1). 正确的复用cell。
2). 设计统一规格的Cell
3). 提前计算并缓存好高度(布局),因为heightForRowAtIndexPath:是调用最频繁的方法;
4). 异步绘制,遇到复杂界面,遇到性能瓶颈时,可能就是突破口;
4). 滑动时按需加载,这个在大量图片展示,网络加载的时候很管用!
5). 减少子视图的层级关系
6). 尽量使所有的视图不透明化以及做切圆操作。
7). 不要动态的add 或者 remove 子控件。最好在初始化时就添加完,然后通过hidden来控制是否显示。
8). 使用调试工具分析问题。
14.delegate 和 notification 的区别
1). 二者都用于传递消息,不同之处主要在于一个是一对一的,另一个是一对多的。
2). notification通过维护一个array,实现一对多消息的转发。
3). delegate需要两者之间必须建立联系,不然没法调用代理的方法;notification不需要两者之间有联系。
15.您一般是怎么用 Instruments 的?
Time Profiler:性能分析
Zombies:检查是否访问了僵尸对象,但是这个工具只能从上往下检查,不智能
Allocations:用来检查内存,写算法的那批人也用这个来检查
Leaks:检查内存,看是否有内存泄露
Core Animation:
1)查看帧数 <55就得优化 优化。
Core Animation工具中有几个和离屏渲染相关的检查选项:
2)Color Offscreen-Rendered Yellow
开启后会把那些需要离屏渲染的图层高亮成黄色,这就意味着黄色图层可能存在性能问题。
3)Color Hits Green and Misses Red
如果shouldRasterize被设置成YES,对应的渲染结果会被缓存,如果图层是绿色,就表示这些缓存被复用;如果是红色就表示缓存会被重复创建,这就表示该处存在性能问题了。
16.您开发常用的工具有哪些?
1).友盟统计
2).青花瓷:这个软件还是蛮不错的,可以用来过滤网络请求,模拟低速网路,还可以修改网络请求内容这些
3).Reveal:调试页面不错,还有用来学习别人的demo时候可以拿来看UI层次结构,还可以用
17.您是怎么调试iOS程序的(谈谈您的iOS调试经验)
断点调试,让程序在执行某一行代码是停止下来,然后来检查当前程序是否正常。断点的种类很多,可以帮助我们快速定位到问题发生时的上下文。
- 普通断点
- 符号断点
- 异常断点
- watch断点
- 条件断点
运行时变量:查看运行时变量值
运行时堆栈:查看函数的调用关系,顺序
日志:通过在程序中添加NSLog代码,在控件台中输出显示日志。
静态代码检查:通过对代码静态分析,找出代码潜在的错误,如内存泄漏、空引用、未使用函数等。
动态分析:通过Instruments工具跟踪分析程序运行时的数据
18.有哪些常见的 Crash 场景?
访问了僵尸对象
访问野指针
访问了不存在的方法
数组越界
在定时器下一次回调前将定时器释放
19.如何调试BAD_ACCESS错误
1)重写object的respondsToSelector方法,现实出现EXEC_BAD_ACCESS前访问的最后一个object
2)通过 Zombie
3)设置全局断点快速定位问题代码所在行
4)Xcode 7 已经集成了BAD_ACCESS捕获功能:Address Sanitizer。 用法如下:在配置中勾选✅
Enable Address Sanitizer
20.lldb(gdb)常用的调试命令?
breakpoint 设置断点定位到某一个函数
n 断点指针下一步
po打印对象
21.如果在Cocoa中发现一个Bug,你会如何处理?
复现bug,确认bug发生的软硬件环境,详细描述后发报告给Apple。
22.CocoaPods的原理
CocoaPods的原理是将所有的依赖库都放到另一个名为Pods的项目中,然后让主项目依赖Pods项目,这样,源码管理工作都从主项目移到了Pods项目中。Pods项目最终会编译成一个名为libPods.a的文件,主项目只需要依赖这个.a文件即可。
23.请用预处理指令#define声明一个常数,用以表明1年中有多少秒(忽略闰年问题)
#define SECONDS_PER_YEAR (60*60*24*365)_U_LONG
24.volatile表示变量随时可以改变
25.请谈谈\#include与\#import的区别、\#import与@class 的区别
#include和#import 其效果相同,都是导入类中定义的行为(方法);
#import 不会引起交叉编译,确保头文件只会被导入一次;
@class 表明只定义了类的名称,而具体类的行为是未知的,一般用于.h 文件
@class比#import编译效率更高。此外@class和#import的主要区别在于解决引用死锁的问题。
26.什么是id类型,id 声明的对象有什么特性?
id 声明的对象具有运行时的特性,即可以指向任意类型的objcetive-c的对象;
27.请谈一谈关键字self、super的作用
self:当前消息的接收者。
super:向父类发送消息。
28.请解释self = [super init]方法
容错处理,当父类初始化失败,会返回一个nil,表示初始化失败。由于继承的关系,子类是需要拥有父类的实例和行为,因此,我们必须先初始化父类,然后再初始化子类
29.请说明如何使用Instancetype及其重要性
instancetype,编译器能正确的推断出返回实例的实际类型!
instancetype只能用于返回值!
建议在返回self实例的方法中,用instancetype,别用id.
30.请问@property中有哪些属性关键字?
原子性---atomic/nonatomic 读/写权限---readwrite(读写)、readonly(只读) 、内存管理语义---assign、strong、weak、unsafe_unretained、copy、方法名---getter=和setter=、不常用的:nonnull,null_resettable,nullable
31.ARC下,不显式指定任何属性关键字时,默认的关键字都有哪些?
atomic,readwrite,assign,strong
32.@protocol 和 category 中如何使用 @property
在 protocol 中使用 property 只会生成 setter 和 getter 方法声明,我们使用属性的目的,是希望遵守我协议的对象能实现该属性
category 使用 @property 也是只会生成 setter 和 getter 方法的声明,如果我们真的需要给 category 增加属性的实现,需要借助于运行时的两个函数:
objc_setAssociatedObject
objc_getAssociatedObject
33.这个写法会出什么问题: @property (copy) NSMutableArray *array;
两个问题:
添加,删除,修改数组内的元素的时候,程序会因为找不到对应的方法而崩溃.因为 copy 就是复制一个不可变 NSArray 的对象;
使用了 atomic 属性会严重影响性能 ;
34.@synthesize合成实例变量的规则是什么?假如property名为foo,存在一个名为_foo的实例变量,那么还会自动合成新变量么?
总结下 @synthesize 合成实例变量的规则,有以下几点:
如果指定了成员变量的名称,会生成一个指定的名称的成员变量,
如果这个成员已经存在了就不再生成了.
如果是 @synthesize foo;
还会生成一个名称为foo的成员变量,也就是说:
如果没有指定成员变量的名称会自动生成一个属性同名的成员变量,
如果是 @synthesize foo = _foo;
就不会生成成员变量了.
假如 property 名为 foo,存在一个名为 _foo的实例变量,那么还会自动合成新变量么? 不会。
34.如何为 Class 定义一个对外只读对内可读写的属性?
在头文件中将属性定义为readonly, 在.m文件中将属性重新定义为readwrite
35.在一个对象的方法里面:`self.name = @"object";`和`name =@"object";`有什么不同吗?
self.name = @"object";会调用对象的setName()方法.
name =@"object";会直接把@"object"赋值给当前对象的name 属性。
36.__block vs __weak
block 不能修改局部变量,如果需要修改需要加上block.
block 会对对象强引用,引起retain-cycle,需要使用weak
37.使用block有什么好处?请使用`NSTimer`写出一个使用block显示(在`UILabel`上)秒表的代码
//iOS10 才有效
NSTimer*timer=[NSTimerscheduledTimerWithTimeInterval:1.0repeats:YEScallback:^(){weakSelf.secondsLabel.text=...}
[[NSRunLoopcurrentRunLoop]addTimer:timerforMode:NSRunLoopCommonModes];
38.谈谈block使用时的注意点?
在block内部使用外部指针且会造成循环引用情况下,需要用weak修饰外部指针weak typeof(self) weakSelf = self;
在block内部如果调用了延时函数还使用弱指针会取不到该指针,因为已经被销毁了,需要在block内部再将弱指针重新强引用一下__strong typeof(self) strongSelf = weakSelf;
如果需要在block内部改变外部变量的话,需要在用__block修饰外部变量
39.block和代理的区别,哪个更好?
代理回调更面向过程,block更面向结果。FBRetainCycleDetector 检测循环引用。
如果你使用一些参数中可能含有 ivar 的系统 api ,如 GCD 、NSNotificationCenter就要小心一点:比如GCD 内部如果引用了 self,而且 GCD 的其他参数是 iva
_operationsQueue 这个
__weak__typeof__(self)weakSelf =self;dispatch_group_async(_operationsGroup, _operationsQueue, ^{ __typeof__(self)strongSelf= weakSelf;[strongSelfdoSomething];[strongSelfdoSomethingElse];} );
__weak__typeof__(self) weakSelf =self;_observer = [[NSNotificationCenterdefaultCenter] addObserverForName:@"testKey"object:nilqueue:nilusingBlock:^(NSNotification*note) { __typeof__(self) strongSelf = weakSelf; [strongSelf dismissModalViewControllerAnimated:YES]; }];
40.类别和类扩展的区别
category和extensions的不同在于后者可以添加属性。另外后者添加的方法是必须要实现的。
extensions可以认为是一个私有的Category。
41.分类的作用?分类和继承的区别?
分类可以在不获悉,不改变原来代码的情况下往里面添加新的方法,只能添加,不能删除修改,并且如果分类和原来类中的方法产生名称冲突,则分类将覆盖原来的方法,因为分类具有更高的优先级。
继承可以增加,修改或者删除方法,并且可以增加属性;但是分类只能添加方法,不能删除修改,也不能增加属性。
42.iOS Extension 是什么?能列举几个常用的 Extension 么?
Extension是Category的一个特例,没有分类名字,可以扩展属性,成员变量和方法。
常用的扩展是在.m文件中声明私有属性和方法,基本上我们天天都在用。
43.addObserver:forKeyPath:options:context:各个参数的作用分别是什么,observer中需要实现哪个方法才能获得KVO回调?
// 添加键值观察/*
1 观察者,负责处理监听事件的对象
2 观察的属性
3 观察的选项
4 上下文
*/[self.personaddObserver:selfforKeyPath:@"name"options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOldcontext:@"Person Name"];
// 所有的 kvo 监听到事件,都会调用此方法/*
1. 观察的属性
2. 观察的对象
3. change 属性变化字典(新/旧)
4. 上下文,与监听的时候传递的一致
*/- (void)observeValueForKeyPath:(NSString *)keyPathofObject:(id)objectchange:(NSDictionary *)changecontext:(void*)context;
44.如何手动触发一个value的KVO
那么“手动触发”的使用场景是什么?一般我们只在希望能控制“回调的调用时机”时才会这么做。
具体做法如下:
如果这个 value 是 表示时间的 self.now ,那么代码如下:最后两行代码缺一不可。
@property (nonatomic, strong) NSDate *now;
- (void)viewDidLoad{
[super viewDidLoad];
[self willChangeValueForKey:@"now"];// “手动触发self.now的KVO”,必写。
[self didChangeValueForKey:@"now"];// “手动触发self.now的KVO”,必写。
}
45.若一个类有实例变量NSString *_foo,调用setValue:forKey:时,可以以foo还是_foo作为key?
可以。kvc机制。
46.KVC的keyPath中的集合运算符如何使用?
必须用在集合对象上或普通对象的集合属性上
简单集合运算符有@avg, @count , @max , @min ,@sum,
格式 @"@sum.age"或 @"集合属性[email protected]"
http://www.cnblogs.com/panda1024/p/6014399.html
47.KVC和KVO的keyPath一定是属性么?
KVO支持实例变量
48.如何关闭默认的KVO的默认实现,并进入自定义的KVO实现?
http://tech.glowing.com/cn/implement-kvo/
49.apple用什么方式实现对一个对象的KVO?
KVO 在实现中通过isa 混写(isa-swizzling)把这个对象的 isa 指针 ( isa 指针告诉 Runtime 系统这个对象的类是什么 ) 指向这个新创建的子类,对象就神奇的变成了新创建的子类的实例
50.代理的作用?
代理的目的是改变或传递控制链。允许一个类在某些特定时刻通知到其他类,而不需要获取到那些类的指针。可以减少框架复杂度。
另外一点,代理可以理解为java中的回调监听机制的一种类似。
51.OC有多继承吗?没有的话用什么代替?
OC中没有多继承,可以用委托代理Protocol来实现
52.什么是 Protocol,Delegate 一般是怎么用的?
Protocol是协议,只有.h文件,甚至可以不独立文件
使用时 遵守协议,在本类中实现方法就可以
协议有required和optional的,required是必须实现的协议方法。
协议声明了可以被任何类实现的方法
协议不是类,它是定义了一个其他对象可以实现的接口
如果在某个类中实现了协议中的某个方法,也就是这个类实现了那个协议。
Delegate:它本身是一个设计模式,它的意思是委托别人去做某事。
53.为什么 NotificationCenter 要 removeObserver? 如何实现自动 remove?
如果不移除的话,万一注册通知的类被销毁以后又发了通知,程序会崩溃.因为向野指针发送了消息
实现自动remove:通过自释放机制,通过动态属性将remove转移给第三者,解除耦合,达到自动实现remove。
54.我们说的oc是动态运行时语言是什么意思?
OC的动态运行时,是指OC具有动态类型和动态绑定的特性。动态类型能使程序直到执行时才确定对象的所属类, 其具体引用的对象在运行时才能确定。 动态绑定能使程序直到运行时才确定调用对象的实际方法。
多态。 主要是将数据类型的确定由编译时,推迟到了运行时。
这个问题其实浅涉及到两个概念,运行时和多态。
简单来说,运行时机制使我们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法。
多态:不同对象以自己的方式响应相同的消息的能力叫做多态。意思就是假设生物类(life)都用有一个相同的方法-eat;
那人类属于生物,猪也属于生物,都继承了life后,实现各自的eat,但是调用是我们只需调用各自的eat方法。
也就是不同的对象以自己的方式响应了相同的消息(响应了eat这个选择器)。
55.objc中向一个nil对象发送消息将会发生什么?
objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,然后在发送消息的时候,objc_msgSend方法不会返回值,所谓的返回内容都是具体调用时执行的。那么,回到本题,如果向一个nil对象发送消息,首先在寻找对象的isa指针时就是0地址返回了,所以不会出现任何错误。
56.objc中向一个对象发送消息[obj foo]和objc_msgSend()函数之间有什么关系?
[obj foo];在objc动态编译时,会被转意为:objc_msgSend(obj,@selector(foo));
57.为什么其他语言里叫函数调用,Object-C里则叫给我对象发消息
在OC中,方法调用是向类发送消息,如[bady cry]在运行时会转换成objc_msgSend(bady,cry),向对象发送消息时根据isa指针找到类,在根据类的调度表查找方法,没找到方法则在父类中查找直至基类,如果始终没有找到返回nil。
Objective-C 的 Runtime 铸就了它动态语言的特性。Objc Runtime使得C具有了面向对象能力,在程序运行时创建,检查,修改类、对象和它们的方法。可以使用runtime的一系列方法实现。
58.isMemberOfClass 和 isKindOfClass 联系与区别
联系:两者都能检测一个对象是否是某个类的成员
区别:isKindOfClass 不仅用来确定一个对象是否是一个类的成员,也可以用来确定一个对象是否派生自该类的类的成员 ,而isMemberOfClass 只能做到第一点。
举例:如 ClassA派 生 自NSObject 类 , ClassA *a = [ClassA alloc] init];,[a isKindOfClass:[NSObject class]] 可以检查出 a 是否是 NSObject派生类 的成员,但 isMemberOfClass 做不到。
59.什么时候会报unrecognized selector的异常?
简单来说:
当调用该对象上某个方法,而该对象上没有实现这个方法的时候,
可以通过“消息转发”进行解决。
简单的流程如下,在上一题中也提到过:
objc是动态语言,每个方法在运行时会被动态转为消息发送,即:objc_msgSend(receiver, selector)。
objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,如果,在最顶层的父类中依然找不到相应的方法时,程序在运行时会挂掉并抛出异常unrecognized selector sent to XXX 。但是在这之前,objc的运行时会给出三次拯救程序崩溃的机会:
Method resolution
objc运行时会调用+resolveInstanceMethod:或者 +resolveClassMethod:,让你有机会提供一个函数实现。如果你添加了函数,那运行时系统就会重新启动一次消息发送的过程,否则 ,运行时就会移到下一步,消息转发(Message Forwarding)。
Fast forwarding
如果目标对象实现了-forwardingTargetForSelector:,Runtime 这时就会调用这个方法,给你把这个消息转发给其他对象的机会。
只要这个方法返回的不是nil和self,整个消息发送的过程就会被重启,当然发送的对象会变成你返回的那个对象。否则,就会继续Normal Fowarding。
这里叫Fast,只是为了区别下一步的转发机制。因为这一步不会创建任何新的对象,但下一步转发会创建一个NSInvocation对象,所以相对更快点。
Normal forwarding
这一步是Runtime最后一次给你挽救的机会。首先它会发送-methodSignatureForSelector:消息获得函数的参数和返回值类型。如果-methodSignatureForSelector:返回nil,Runtime则会发出-doesNotRecognizeSelector:消息,程序这时也就挂掉了。如果返回了一个函数签名,Runtime就会创建一个NSInvocation对象并发送-forwardInvocation:消息给目标对象。
60.一个objc对象的isa的指针指向什么?有什么作用?
指向他的类对象,从而可以找到对象上的方法
61._objc_msgForward函数是做什么的,直接调用它将会发生什么?
_objc_msgForward是 IMP 类型,用于消息转发的:当向一个对象发送一条消息,但它并没有实现的时候,_objc_msgForward会尝试做消息转发。
直接调用_objc_msgForward是非常危险的事,如果用不好会直接导致程序Crash,但是如果用得好,能做很多非常酷的事。
一旦调用_objc_msgForward,将跳过查找 IMP 的过程,直接触发“消息转发”,如果调用了_objc_msgForward,即使这个对象确实已经实现了这个方法,你也会告诉objc_msgSend:“我没有在这个对象里找到这个方法的实现”
62.能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?
不能向编译后得到的类中增加实例变量;
能向运行时创建的类中添加实例变量;
原因如下:
因为编译后的类已经注册在 runtime 中,类结构体中的 objc_ivar_list 实例变量的链表 和 instance_size 实例变量的内存大小已经确定,同时runtime 会调用 class_setIvarLayout 或 class_setWeakIvarLayout 来处理 strong weak 引用。所以不能向存在的类中添加实例变量;
运行时创建的类是可以添加实例变量,调用 class_addIvar 函数。但是得在调用 objc_allocateClassPair 之后,objc_registerClassPair 之前,原因同上。
63.Objective-C 中,meta-class 指的是什么?
meta-class 是 Class 对象的类, 为这个Class类存储类方法, 当一个类发送消息时,就去那个类对应的meta-class中查找那个消息。
每个Class都有不同的meta-class, 所有的meta-class都使用基类的meta-class(假如类继承NSObject,那么他所对应的meta-class也是NSObject) 作为他们的类
63.Toll-Free Bridging 是什么?什么情况下会使用?
Toll-Free Bridging用于在Foundation对象与Core Foundation对象之间交换数据,俗称桥接
在ARC环境下,Foundation对象转成 Core Foundation对象
使用__bridge桥接以后ARC会自动管理2个对象
使用__bridge_retained桥接需要手动释放Core Foundation对象
在ARC环境下, Core Foundation对象转成 Foundation对象
使用__bridge桥接,如果Core Foundation对象被释放,Foundation对象也同时不能使用了,需要手动管理Core Foundation对象
使用__bridge_transfer桥接,系统会自动管理2个对象
63.iOS 是如何管理内存的
Objective-C中的对象都是基于引用计数来管理生命周期的。简单来说就是,我们在需要持有一个对象时,调用retain让它的引用计数+1。不需要这个对象的时候,调用release让它的引用计数-1。当一个对象引用计数为0的时候,这个对象就会被自动销毁
64.ARC通过什么方式帮助开发者管理内存?
ARC相对于MRC,不是在编译时添加retain/release/autorelease这么简单。应该是编译期和运行期两部分共同帮助开发者管理内存。
在编译期,ARC用的是更底层的C接口实现的retain/release/autorelease,这样做性能更好,也是为什么不能在ARC环境下手动retain/release/autorelease,同时对同一上下文的同一对象的成对retain/release操作进行优化(即忽略掉不必要的操作);
ARC也包含运行期组件,这个地方做的优化比较复杂,但也不能被忽略。
65.一个objc对象如何进行内存布局?(考虑有父类的情况)
所有父类的成员变量和自己的成员变量都会存放在该对象所对应的存储空间中.
每一个对象内部都有一个isa指针,指向他的类对象,类对象中存放着本对象的
对象方法列表(对象能够接收的消息列表,保存在它所对应的类对象中)
成员变量的列表,
属性列表,
它内部也有一个isa指针指向元对象(meta class),元对象内部存放的是类方法列表,类对象内部还有一个superclass的指针,指向他的父类对象。
每个 Objective-C 对象都有相同的结构,如下图所示:
翻译过来就是
ISA指针
根类的实例变量
倒数第二层父类的实例变量
...
父类的实例变量
类的实例变量
根对象就是NSobject,它的superclass指针指向nil
类对象既然称为对象,那它也是一个实例。类对象中也有一个isa指针指向它的元类(meta class),即类对象是元类的实例。元类内部存放的是类方法列表,根元类的isa指针指向自己,superclass指针指向NSObject类
66.不手动指定autoreleasepool的前提下,一个autorealese对象在什么时刻释放?(比如在一个vc的viewDidLoad中创建)
分两种情况:手动干预释放时机、系统自动去释放。
手动干预释放时机 - 指定autoreleasepool就是所谓的:当前作用域大括号结束时释放。
系统自动去释放 - 不手动指定autoreleasepool
Autorelease对象出了作用域之后,会被添加到最近一次创建的自动释放池中,并会在当前的 runloop 迭代结束时释放。
如果在一个vc的viewDidLoad中创建一个 Autorelease对象,那么该对象会在 viewDidAppear 方法执行前就被销毁了。
67.苹果是如何实现autoreleasepool的?
autoreleasepool 以一个栈的形式实现,主要通过下列三个函数完成.
objc_autoreleasepoolPush
objc_autoreleasepoolPop
objc_aurorelease
看函数名就可以知道,对 autorelease 分别执行 push,和 pop 操作。销毁对象时执行release操作。
68.请谈谈内存的使用和优化的注意事项
重用问题:如UITableViewCells、UICollectionViewCells、UITableViewHeaderFooterViews设置正确的reuseIdentifier,充分重用;
尽量把views设置为不透明:当opque为NO的时候,图层的半透明取决于图片和其本身合成的图层为结果,可提高性能;
不要使用太复杂的XIB/Storyboard:载入时就会将XIB/storyboard需要的所有资源,包括图片全部载入内存,即使未来很久才会使用。那些相比纯代码写的延迟加载,性能及内存就差了很多;
选择正确的数据结构:学会选择对业务场景最合适的数组结构是写出高效代码的基础。比如,数组: 有序的一组值。使用索引来查询很快,使用值查询很慢,插入/删除很慢。字典: 存储键值对,用键来查找比较快。集合: 无序的一组值,用值来查找很快,插入/删除很快。gzip/zip压缩:当从服务端下载相关附件时,可以通过gzip/zip压缩后再下载,使得内存更小,下载速度也更快。
延迟加载:对于不应该使用的数据,使用延迟加载方式。对于不需要马上显示的视图,使用延迟加载方式。比如,网络请求失败时显示的提示界面,可能一直都不会使用到,因此应该使用延迟加载。
数据缓存:对于cell的行高要缓存起来,使得reload数据时,效率也极高。而对于那些网络数据,不需要每次都请求的,应该缓存起来,可以写入数据库,也可以通过plist文件存储。
处理内存警告:一般在基类统一处理内存警告,将相关不用资源立即释放掉重用大开销对象:一些objects的初始化很慢,比如NSDateFormatter
和NSCalendar,但又不可避免地需要使用它们。通常是作为属性存储起来,防止反复创建。
避免反复处理数据:许多应用需要从服务器加载功能所需的常为JSON或者XML格式的数据。在服务器端和客户端使用相同的数据结构很重要;
使用Autorelease Pool:在某些循环创建临时变量处理数据时,自动释放池以保证能及时释放内存;
正确选择图片加载方式:详情阅读UIImage加载方式
69.dispatch_barrier_async的作用是什么?
在并行队列中,为了保持某些任务的顺序,需要等待一些任务完成后才能继续进行,使用 barrier 来等待之前任务完成,避免数据竞争等问题。
dispatch_barrier_async 函数会等待追加到Concurrent Dispatch Queue并行队列中的操作全部执行完之后,然后再执行 dispatch_barrier_async 函数追加的处理,等 dispatch_barrier_async 追加的处理执行结束之后,Concurrent Dispatch Queue才恢复之前的动作继续执行。
70.苹果为什么要废弃dispatch_get_current_queue?
dispatch_get_current_queue容易造成死锁
71.如何用GCD同步若干个异步调用?(如根据若干个url异步加载多张图片,然后在都下载完成后合成一张整图)
使用Dispatch Group追加block到Global Group Queue,这些block如果全部执行完毕,就会执行Main Dispatch Queue中的结束处理的block
dispatch_queue_tqueue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_group_tgroup= dispatch_group_create();
dispatch_group_async(group,queue, ^{/*加载图片1 */});
dispatch_group_async(group,queue, ^{/*加载图片2 */});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{// 合并图片});
73.什么是 Runloop?RunLoop都有些模式?为什么 UIScrollView 的滚动会导致 NSTimer 失效?
model 主要是用来指定事件在运行循环中的优先级的,分为:
NSDefaultRunLoopMode(kCFRunLoopDefaultMode):默认,空闲状态
UITrackingRunLoopMode:ScrollView滑动时
UIInitializationRunLoopMode:启动时
NSRunLoopCommonModes(kCFRunLoopCommonModes):Mode集合
苹果公开提供的 Mode 有两个:
NSDefaultRunLoopMode(kCFRunLoopDefaultMode)
NSRunLoopCommonModes(kCFRunLoopCommonModes)
定时器里面有个runoop mode,一般定时器是运行在defaultmode上。但是如果滑动了这个页面,主线程runloop会转到UITrackingRunLoopMode中,这时候就不能处理定时器了,造成定时器失效,原因就是runroop mode选错了,解决办法有2个
一个是更改mode为NSRunLoopCommonModes(无论runloop运行在哪个mode,都能运行),
还有种办法是切换到主线程来更新UI界面的刷新
74.UITableViewCell上有个UILabel,显示NSTimer实现的秒表时间,手指滚动cell过程中,label是否刷新,为什么?
这是否刷新取决于timer加入到Run Loop中的Mode是什么。
75.猜想runloop内部是如何实现的?
functionloop()
{ initialize();
do{
var message = get_next_message();
process_message(message);
}while(message != quit);}
int main(intargc,char* argv[]){
//程序一直运行状态
while(AppIsRunning) {
//睡眠状态,等待唤醒事件
id whoWakesMe = SleepForWakingUp();
//得到唤醒事件
idevent= GetEvent(whoWakesMe);
//开始处理事件
HandleEvent(event);
}return0;}
76.runloop和线程有什么关系?
主线程的run loop默认是启动的。
对其它线程来说,run loop默认是没有启动的,如果你需要更多的线程交互则可以手动配置和启动,如果线程只是去执行一个长时间的已确定的任务则不需要。
在任何一个 Cocoa 程序的线程中,都可以通过以下代码来获取到当前线程的 run loop 。
run loop和线程是紧密相连的,可以这样说run loop是为了线程而生,没有线程,它就没有存在的必要。Run loops是线程的基础架构部分
77.iOS 7的多任务添加了哪两个新的 API? 各自的使用场景是什么?
后台获取(Background Fetch):后台获取使用场景是用户打开应用之前就使app有机会执行代码来获取数据,刷新UI。这样在用户打开应用的时候,最新的内容将已然呈现在用户眼前,而省去了所有的加载过程。
推送唤醒(Remote Notifications):使用场景是使设备在接收到远端推送后让系统唤醒设备和我们的后台应用,并先执行一段代码来准备数据和UI,然后再提示用户有推送。这时用户如果解锁设备进入应用后将不会再有任何加载过程,新的内容将直接得到呈现。
78.请解释一下Handoff是什么,并简述它是如何实现iOS、Mac/网页应用互通的。
Handoff 是在 OS X 和 iOS 跨设备连续性的拓展的用户体验的功能。Handoff 使用户可以在一台设备中开始一个活动,然后切换到其他设备并在该设备恢复相同的活动。例如,某个用户从 Safari 移到登陆了相同的 Apple ID 账户的 iOS 设备浏览一篇很长的文章,相同的页面会在 iOS 的 Safari 打开,并且滚动条位置和启始设备相同,Handoff 使这个体验尽可能平稳顺畅。
79.内存管理主要分两种模式,MRC 和ARC
1、MRC MRC是手动管理内存,xcode4.1以及一下版本没有ARC
引用计数概念 retain +1 release -1
内存释放池Release Pool:把需要释放的内存统一放在一个池子中,当池子被抽干后(drain),池子中所有的内存空间也被自动释放掉。内存池的释放操作分为自动和手动。自动释放受runloop机制影响。
2、ARC ACR是自动管理内存,自动引用计数 重点:循环引用,用weak 修饰来释放(block,delegate会出现这种情况)