共计 11120 个字符,预计需要花费 28 分钟才能阅读完成。
1. 简略介绍下 NSURLConnection 类及 + sendSynchronousRequest:returningResponse:error:
与– initWithRequest:delegate:
两个办法的区别?
答: NSURLConnection 次要用于网络拜访,其中 + sendSynchronousRequest:returningResponse:error: 是同步拜访数据,即以后线程会阻塞,并期待 request 的返回的 response,而– initWithRequest:delegate: 应用的是异步加载,当其实现网络拜访后,会通过 delegate 回到主线程,并其委托的对象。
2. 在我的项目什么时候抉择应用 GCD,什么时候抉择 NSOperation
答: 我的项目中应用 NSOperation 的长处是 NSOperation 是对线程的高度形象,在我的项目中应用它,会使我的项目的程序结构更好,子类化 NSOperation 的设计思路,是具备面向对象的长处 (复用、封装),使得实现是多线程反对,而接口简略,倡议在简单我的项目中应用。
我的项目中应用 GCD 的长处是 GCD 自身非常简单、易用,对于不简单的多线程操作,会节俭代码量,而 Block 参数的应用,会是代码更为易读,倡议在简略我的项目中应用。
3. ViewController 的 didReceiveMemoryWarning 怎么被调用
答:[supper didReceiveMemoryWarning];
4. 写一个 setter 办法用于实现@property(nonatomic, retain) NSString *name
, 写一个 setter 办法用于实现@property(nonatomic, copy) NSString *name
- (void)setName:(NSString *)str{[str retain];
[_name release];
_name = str;
}
- (void)setName:(NSString *)str{id t = [str copy];
[_name release];
_name = t;
}
5. 对于语句NSString *obj = [[NSData alloc] init];
obj 在编译时和运行时别离时什么类型的对象?
答:编译时是 NSString 的类型; 运行时是 NSData 类型的对象
作为一个 ios 开发者,遇到问题的时候,有一个学习的气氛跟一个交换圈子特地重要对本身有很大帮忙,众人拾柴火焰高 这是一个我的 iOS 交换群:711315161,分享 BAT, 阿里面试题、面试教训,探讨技术,大家一起交流学习成长!心愿帮忙开发者少走弯路。
6. Object C 中创立线程的办法是什么? 如果在主线程中执行代码,办法是什么? 如果想延时执行代码、办法又是什么?
答:线程创立有三种办法:应用 NSThread 创立、应用 GCD 的 dispatch、应用子类化的 NSOperation, 而后将其退出 NSOperationQueue; 在主线程执行代码,办法是 performSelectorOnMainThread,如果想延时执行代码能够用 performSelector:onThread:withObject:waitUntilDone:
7. 浅复制和深复制的区别?
答:浅层复制:只复制指向对象的指针,而不复制援用对象自身。
深层复制:复制援用对象自身。
8. PerformSelecter
当调用 NSObject 的
performSelecter:afterDelay:
后,实际上其外部会创立一个 Timer 并增加到以后线程的 RunLoop 中。所以如果以后线程没有 RunLoop,则这个办法会生效。
当调用performSelector:onThread:
时,实际上其会创立一个 Timer 加到对应的线程去,同样的,如果对应线程没有 RunLoop 该办法也会生效。
9. 优化你是从哪几方面着手?
一、首页启动速度
启动过程中做的事件越少越好(尽可能将多个接口合并)
不在 UI 线程上作耗时的操作(数据的解决在子线程进行,解决完告诉主线程刷新节目)
在适合的机会开始后台任务(例如在用户指引节目就能够开始筹备加载的数据)
二、页面浏览速度
json 的解决(iOS 自带的 NSJSONSerialization,Jsonkit,SBJson)
数据的分页(后端数据多的话,就要分页返回,例如网易新闻,或者 微博记录)
数据压缩(大数据也能够压缩返回,缩小流量,放慢反应速度)
内容缓存(例如网易新闻的最新新闻列表都是要缓存到本地,从本地加载,能够缓存到内存,或者数据库,依据状况而定)
延时加载 tab(比方 app 有 5 个 tab,能够先加载第一个要显示的 tab,其余的在显示时候加载,按需加载)
算法的优化(外围算法的优化,例如有些 app 有个 联系人姓名用汉语拼音的首字母排序)
三、操作晦涩度优化
Tableview 优化(tableview cell 的加载优化)
ViewController 加载优化(不同 view 之间的跳转,能够提前准备好数据)
四、数据库的优化
数据库设计下面的重构
查问语句的优化
分库分表(数据太多的时候,能够分不同的表或者库)
五、服务器端和客户端的交互优化
客户端尽量减少申请
服务端尽量做多的逻辑解决
服务器端和客户端采取推拉联合的形式(能够利用一些同步机制)
通信协议的优化(缩小报文的大小)
电量应用优化(尽量不要应用后盾运行)
六、非技术性能优化
产品设计的逻辑性(产品的设计肯定要合乎逻辑,或者逻辑尽量简略,否则会让程序员抓狂,有时候用了好大力量,才能够实现一个小小的逻辑设计问题)
界面交互的标准(每个模块的界面的交互尽量对立,合乎操作习惯)
代码标准(这个能够隐形带来 app 性能的进步,比方 用 if else 还是 switch,或者是用!还是 ==)
code review(保持 code Review 继续重构代码。缩小代码的逻辑复杂度)
10. 什么状况应用 weak 关键字,相比 assign 有什么不同?
1. 在 ARC 中, 在有可能呈现循环援用的时候, 往往要通过让其中一端应用 weak 来解决, 比方: delegate 代理属性。
2. 本身曾经对它进行一次强援用, 没有必要再强援用一次, 此时也会应用 weak, 如自定义 IBOutlet 控件属性个别也应用 weak;当然,也能够应用 strong。
IBOutlet 连进去的视图属性为什么能够被设置成 weak?
答:因为父控件的 subViews 数组曾经对它有一个强援用。
不同点
assign 能够用非 OC 对象,而 weak 必须用于 OC 对象。
weak 表明该属性定义了一种“非领有关系”。在属性所指的对象销毁时,属性值会主动清空(nil)
11. 用 @property 申明的 NSString / NSArray / NSDictionary 常常应用 copy 关键字,为什么?如果改用 strong 关键字,可能造成什么问题?
答:用 @property 申明 NSString、NSArray、NSDictionary 常常应用 copy 关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操作(就是把可变的赋值给不可变的),为确保对象中的字符串值不会无意间变动,应该在设置新属性值时拷贝一份。
1. 因为父类指针能够指向子类对象, 应用 copy 的目标是为了让本对象的属性不受外界影响, 应用 copy 无论给我传入是一个可变对象还是不可对象, 我自身持有的就是一个不可变的正本。
2. 如果咱们应用是 strong , 那么这个属性就有可能指向一个可变对象, 如果这个可变对象在内部被批改了, 那么会影响该属性。
总结:应用 copy 的目标是,避免把可变类型的对象赋值给不可变类型的对象时,可变类型对象的值发送变动会无意间篡改不可变类型对象原来的值。
12. runtime 如何实现 weak 变量的主动置 nil?
runtime 对注册的类,会进行布局,会将 weak 对象放入一个 hash 表中。用 weak 指向的对象内存地址作为 key,当此对象的援用计数为 0 的时候会调用对象的 dealloc 办法,假如 weak 指向的对象内存地址是 a,那么就会以 a 为 key,在这个 weak hash 表中搜寻,找到所有以 a 为 key 的 weak 对象,从而设置为 nil
13. runloop 是什么/runloop 的概念?
runloop 是线程相干的根底框架的一部分。一个 runloop 就是一个事件处理的循环,用来不停的调度工作以及解决输出事件。其实外部就是 do-while 循环,这个循环外部一直地解决各种工作(比方 Source,Timer,Observer)。应用 runloop 的目标是让你的线程在有工作的时候忙于工作,而没工作的时候处于休眠状态。
14. UITableViewCell 上有个 UILabel,显示 NSTimer 实现的秒表工夫,手指滚动 cell 过程中,label 是否刷新,为什么?
这是否刷新取决于 timer 退出到 Run Loop 中的 Mode 是什么。Mode 次要是用来指定事件在运行循环中的优先级的,分为
- NSDefaultRunLoopMode(kCFRunLoopDefaultMode):默认,闲暇状态
- UITrackingRunLoopMode:ScrollView 滑动时会切换到该 Mode
- UIInitializationRunLoopMode:run loop 启动时,会切换到该 mode
- NSRunLoopCommonModes(kCFRunLoopCommonModes):Mode 汇合
苹果公开提供的 Mode 有两个- NSDefaultRunLoopMode(kCFRunLoopDefaultMode)
- NSRunLoopCommonModes(kCFRunLoopCommonModes)
在编程中:如果咱们把一个 NSTimer 对象以 NSDefaultRunLoopMode(kCFRunLoopDefaultMode)增加到主运行循环中的时候, ScrollView 滚动过程中会因为 mode 的切换,而导致 NSTimer 将不再被调度。当咱们滚动的时候,也心愿不调度,那就应该应用默认模式。然而,如果心愿在滚动时,定时器也要回调,那就应该应用 common mode。
15. NStimer 准吗?谈谈你的认识?如果不准该怎么实现一个准确的 NSTimer?
不准;不准的起因如下
1、NSTimer 加在 main runloop 中,模式是 NSDefaultRunLoopMode,main 负责所有主线程事件,例如 UI 界面的操作,简单的运算,这样在同一个 runloop 中 timer 就会产生阻塞。
2、模式的扭转。主线程的 RunLoop 里有两个预置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。
当你创立一个 Timer 并加到 DefaultMode 时,Timer 会失去反复回调,但此时滑动一个 ScrollView 时,RunLoop 会将 mode 切换为 TrackingRunLoopMode,这时 Timer 就不会被回调,并且也不会影响到滑动操作。所以就会影响到 NSTimer 不准的状况。
PS:DefaultMode 是 App 平时所处的状态,rackingRunLoopMode 是追踪 ScrollView 滑动时的状态。
办法:
1、在主线程中进行 NSTimer 操作,然而将 NSTimer 实例加到 main runloop 的特定 mode(模式)中。防止被简单运算操作或者 UI 界面刷新所烦扰 self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(showTime) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
2、在子线程中进行 NSTimer 的操作,再在主线程中批改 UI 界面显示操作后果
-(void)timerMethod2 {
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(newThread) object:nil];
[thread start];
}
-(void)newThread{
@autoreleasepool{
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(showTime) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] run];
}
}
16. NSOperation 相比于 GCD 有哪些劣势?
GCD 是基于 c 的底层 api,NSOperation 属于 object- c 类。ios 首先引入的是 NSOperation,IOS4 之后引入了 GCD 和 NSOperationQueue 并且其外部是用 gcd 实现的。
绝对于 GCD:
1、NSOperation 领有更多的函数可用,具体查看 api。
2、在 NSOperationQueue 中,能够建设各个 NSOperation 之间的依赖关系。
3、有 kvo 能够监测 operation 是否正在执行(isExecuted)、是否完结(isFinished),是否勾销(isCanceld)。
4、NSOperationQueue 能够不便的治理并发、NSOperation 之间的优先级。
GCD 次要与 block 联合应用。代码简洁高效。
GCD 也能够实现简单的多线程利用,次要是建设个个线程工夫的依赖关系这类的状况,然而须要本人实现相比 NSOperation 要简单。
具体应用哪个,依需要而定。从集体应用的感觉来看,比拟适合的用法是:除了依赖关系尽量应用 GCD,因为苹果专门为 GCD 做了性能下面的优化。
17. 如何拜访并批改一个类的公有属性?
有两种办法能够拜访公有属性, 一种是通过 KVC 获取, 一种是通过 runtime 拜访并批改公有属性。
18. 如何捕捉异样?
1\. 在 app 启动时(didFinishLaunchingWithOptions),增加一个异样捕捉的监听
NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
2\. 实现捕捉异样日志并保留到本地的办法
void UncaughtExceptionHandler(NSException *exception){
// 异样日志获取
NSArray *excpArr = [exception callStackSymbols];
NSString *reason = [exception reason];
NSString *name = [exception name];
NSString *excpCnt = [NSString stringWithFormat:@"exceptionType: %@ \n reason: %@ \n stackSymbols: %@",name,reason,excpArr];
// 日常日志保留(能够将此性能独自提炼到一个办法中)NSArray *dirArr = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *dirPath = dirArr[0];
NSString *logDir = [dirPath stringByAppendingString:@"/CrashLog"];
BOOL isExistLogDir = YES;
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:logDir]) {isExistLogDir = [fileManager createDirectoryAtPath:logDir withIntermediateDirectories:YES attributes:nil error:nil];
}
if (isExistLogDir) {
// 此处可扩大
NSString *logPath = [logDir stringByAppendingString:@"/crashLog.txt"];
[excpCnt writeToFile:logPath atomically:YES encoding:NSUTF8StringEncoding error:nil];
}
}
19. Object- c 的类能够多重继承么? 能够实现多个接口么?Category 是什么? 重写一个类的形式用继承好还是分类好? 为什么?
答:Object- c 的类不能够多重继承; 能够实现多个接口,通过实现多个接口能够实现 C ++ 的多重继承;Category 是类别,个别状况用分类好,用 Category 去重写类的办法,仅对本 Category 无效,不会影响到其余类与原有类的关系。
20. Category(分类),Extension(扩大)和继承的区别
答:1. 分类
category 原则上只能在现有类根底上增加新的办法(能增加属性的起因只是通过 runtime 解决无 setter/getter 的问题而已),类别中的办法没被实现编译器是不会有任何正告的,这是因为类别是在运行时增加到类中的
2. 扩大
iOS 中的 extension 就是匿名的分类,只有头文件没有实现文件。类扩大不仅能够减少办法,还能够减少实例变量(或者属性),只是该实例变量默认是 @private 类型的(应用范畴只能在本身类,而不是子类或其余中央),类扩大中申明的办法没被实现,编译器会报警,这是因为类扩大是在编译阶段被增加到类中的
3. 继承
在 iOS 中继承是单继承,既只能有一个父类。在继承中,子类能够应用父类的办法和变量,当子类想对本类或者父类的变量进行初始化,那么须要重写 init()办法。父类也能够拜访子类的办法和成员变量
21. 简述内存分区状况
1). 代码区:寄存函数二进制代码
2). 数据区:零碎运行时申请内存并初始化,零碎退出时由零碎开释。寄存全局变量、动态变量、常量
3). 堆区:通过 malloc 等函数或 new 等操作符动静申请失去,需程序员手动申请和开释
4). 栈区:函数模块内申请,函数完结时由零碎主动开释。寄存局部变量、函数参数
22. 间接调用_objc_msgForward 函数将会产生什么?
_objc_msgForward 是 IMP 类型,用于音讯转发的:当向一个对象发送一条音讯,但它并没有实现的时候,_objc_msgForward 会尝试做音讯转发。
间接调用_objc_msgForward 是十分危险的事,如果用不好会间接导致程序 Crash,然而如果用得好,能做很多十分酷的事。
一旦调用_objc_msgForward,将跳过查找 IMP 的过程,间接触发“音讯转发”,如果调用了_objc_msgForward,即便这个对象的确曾经实现了这个办法,你也会通知 objc_msgSend:“我没有在这个对象里找到这个办法的实现”
23. 对于 Run Loop 的了解
- RunLoop,是多线程的法宝,即一个线程一次只能执行一个工作,执行完工作后就会退出线程。主线程执行完即时工作时会持续期待接管事件而不退出。非主线程通常来说就是为了执行某一工作的,执行结束就须要偿还资源,因而默认是不运行 RunLoop 的;
- 每一个线程都有其对应的 RunLoop,只是默认只有主线程的 RunLoop 是启动的,其它子线程的 RunLoop 默认是不启动的,若要启动则须要手动启动;
- 在一个独自的线程中,如果须要在解决完某个工作后不退出,持续期待接管事件,则须要启用 RunLoop;
- NSRunLoop 提供了一个增加 NSTimer 的办法,能够指定 Mode,如果要让任何状况下都回调,则须要设置 Mode 为 Common 模式;
- 本质上,对于子线程的 runloop 默认是不存在的,因为苹果采纳了懒加载的形式。如果咱们没有手动调用 [NSRunLoop currentRunLoop] 的话,就不会去查问是否存在以后线程的 RunLoop,也就不会去加载,更不会创立。
24. runtime 如何通过 selector 找到对应的 IMP 地址?(别离思考类办法和实例办法)
1. 每一个类对象中都一个对象办法列表(对象办法缓存)
2. 类办法列表是寄存在类对象中 isa 指针指向的元类对象中(类办法缓存)
3. 办法列表中每个办法构造体中记录着办法的名称, 办法实现, 以及参数类型,其实 selector 实质就是办法名称, 通过这个办法名称就能够在办法列表中找到对应的办法实现.
4. 当咱们发送一个音讯给一个 NSObject 对象时,这条音讯会在对象的类对象办法列表里查找
5. 当咱们发送一个音讯给一个类时,这条音讯会在类的 Meta Class 对象的办法列表里查找
25. runtime 中,SEL 和 IMP 的区别
办法名 SEL – 示意该办法的名称;
IMP – 指向该办法的具体实现的函数指针,说白了 IMP 就是实现办法。
26.block 底层实现
block 实质是指向一个构造体的一个指针
运行时机制 比拟高级的个性 纯 C 语言
平时写的 OC 代码 转换成 C 语言运行时的代码
指令:clang -rewrite-objc main.m(能够打印验证)
默认状况下, 任何 block 都是在栈外面的, 随时可能被回收
只有对其做一次 copy 操作 block 的内存就会放在堆外面 不会开释
只有 copy 能力产生一个新的内存地址 所有地址会产生扭转
27. TCP 协定三次握手
TCP 协定采纳了三次握手策略。用 TCP 协定把数据包送出去后,TCP 不会对传送后的状况束之高阁,它肯定会向对方确认是否胜利送达。握手过程中应用了 TCP 的标记——SYN(synchronize)和 ACK(acknowledgement)。发送端首先发送一个带 SYN 标记的数据包给对方。接收端收到后,回传一个带有 SYN/ACK 标记的数据包以示传播确认信息。最初,发送端再回传一个带 ACK 标记的数据包,代表“握手”完结。
28. @property 的实质是什么?
@property = ivar + getter + setter;
“属性”(property)有两大概念:ivar(实例变量)、getter+setter(存取方法)
29. KVC 的底层实现?
当一个对象调用 setValue 办法时,办法外部会做以下操作:
1). 查看是否存在相应的 key 的 set 办法,如果存在,就调用 set 办法。
2). 如果 set 办法不存在,就会查找与 key 雷同名称并且带下划线的成员变量,如果有,则间接给成员变量属性赋值。
3). 如果没有找到_key,就会查找雷同名称的属性 key,如果有就间接赋值。
4). 如果还没有找到,则调用 valueForUndefinedKey: 和 setValue:forUndefinedKey: 办法。
这些办法的默认实现都是抛出异样,咱们能够依据须要重写它们。
30. ViewController 生命周期
依照执行顺序排列:
1). initWithCoder:通过 nib 文件初始化时触发。
2). awakeFromNib:nib 文件被加载的时候,会产生一个 awakeFromNib 的音讯到 nib 文件中的每个对象。
3). loadView:开始加载视图控制器自带的 view。
4). viewDidLoad:视图控制器的 view 被加载实现。
5). viewWillAppear:视图控制器的 view 将要显示在 window 上。
6). updateViewConstraints:视图控制器的 view 开始更新 AutoLayout 束缚。
7). viewWillLayoutSubviews:视图控制器的 view 将要更新内容视图的地位。
8). viewDidLayoutSubviews:视图控制器的 view 曾经更新视图的地位。
9). viewDidAppear:视图控制器的 view 曾经展现到 window 上。
10). viewWillDisappear:视图控制器的 view 将要从 window 上隐没。
11). viewDidDisappear:视图控制器的 view 曾经从 window 上隐没。
31. 如何用 GCD 同步若干个异步调用?(如依据若干个 url 异步加载多张图片,而后在都下载实现后合成一张整图)
// 应用 Dispatch Group 追加 block 到 Global Group Queue, 这些 block 如果全副执行结束,就会执行 Main Dispatch Queue 中的完结解决的 block。// 创立队列组
dispatch_group_t group = dispatch_group_create();
// 获取全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue, ^{ /* 加载图片 1 */});
dispatch_group_async(group, queue, ^{ /* 加载图片 2 */});
dispatch_group_async(group, queue, ^{ /* 加载图片 3 */});
// 当并发队列组中的工作执行结束后才会执行这里的代码
dispatch_group_notify(group, dispatch_get_main_queue(), ^{// 合并图片});
32. dispatch_barrier_async(栅栏函数)的作用是什么?
函数定义:dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
作用:1. 在它后面的工作执行完结后它才执行,它前面的工作要等它执行实现后才会开始执行。2. 防止数据竞争
// 1. 创立并发队列
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
// 2. 向队列中增加工作
dispatch_async(queue, ^{ // 1.2 是并行的
NSLog(@"工作 1, %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{NSLog(@"工作 2, %@",[NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{NSLog(@"工作 barrier, %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{ // 这两个是同时执行的
NSLog(@"工作 3, %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{NSLog(@"工作 4, %@",[NSThread currentThread]);
});
// 输入后果: 工作 1 工作 2 ——》工作 barrier ——》工作 3 工作 4
// 其中的工作 1 与工作 2,工作 3 与工作 4 因为是并行处理先后顺序不定。
作者:72 行代码 链接:iOS 面试题汇总