2021iOS面试题总结day2

准备:
1.iOS的数据持久化方案
所有的本地持久化数据存储的本质都是写文件,而且只能存到沙盒中。
沙盒机制是苹果的一项安全机制,本质就是系统给每个应用分配了一个文件夹来存储数据,而且每个应用只能访问分配给自己的那个文件夹,其他应用的文件夹是不能访问的。

数据存储的核心都是写文件。主要有四种持久化方式:属性列表,对象序列化,SQLite 数据库, CoreData

2.常用的SQL语句
insert into values
delete from where
update set where
select from where
3.会哪些前端框架
Vue element-ui
4.Vue的特性
轻量级、双向数据绑定、指令、组件化、客户端路由、状态管理
6.iOS系统架构
从下到上:Core OS(核心操作系统层)、Core Services(核心服务层)、Media (媒体层)、Cocoa Touch(界面层)
5.block
block本质上是一个OC对象,他内部也有一个isa指针。block是封装了函数调用以及函数调用环境的OC对象。
一共有三种block,分别是全局的、栈上的、堆上的。
6.手写一个单例

  • (LoginManager *)sharedLoginManager
    {
    if(!sharedLoginManager){
    sharedLoginManager = [[self allocWithZone:Null]init];
    }
    return sharedLoginManager;
    }

今天遇到的面试题:

1.传值的回调方式有哪些,区别是什么?
答:
1.目标动作对(Targe-Action):这里接受消息的对象是目标(target),消息的选择器(selector)是动作(action),一对一,向一个对象发送一个回调。
2.辅助对象:包括代理(Delegate)和数据源(DataSource),一对一,向一个对象发送多个回调。
3.通知(Notification),一对多,在程序中如果需要触发多个(其他对象中)的回调对象时,可以使用通知的方式来完成。
4.Block:谁要传值谁就定义含有参数的Block, 谁要调用谁就执行这个Block,通过Block对象,可以将回调相关的代码写在同一个代码段中。

总结:这道题我只答出了后三种,忘记了第一种目标动作对,他们的区别我只答出了delegate和Notification的区别是一对一和一对多,对于回调方式的区别没有答出来。

2.block的属性修饰用什么关键字,delegate的属性修饰用什么关键字,为什么?assign和weak的区别?

答:block的属性修饰词是copy,默认情况下block是存放在栈中,可能随时被回收,通过copy操作可以使其在堆中保留一份,相当于一直强引用着,因此如果block中用到self时需要将其进行弱化,通过__weak或者__unsafe_unretained。
delegate的属性修饰关键词是weak,是为了防止循环引用。

weak和assign的区别:
weak 只可以修饰对象。weak不会产生野指针的问题,因为weak修饰的对象被释放后指针会自动置为nil,之后再向该对象发送消息也不会崩溃。weak是安全的。
assign 可修饰对象,和基本数据类型。如果修饰对象,会产生野指针问题;如果修饰基本数据类型则是安全的。修饰的对象释放后,指针不会被自动置空,此时向对象发送消息会崩溃。

总结:这道题我只答出来了delegate的属性修饰词用weak,为了防止循环引用,block的属性修饰词答成了weak,weak是block中self的修饰词,不是block的修饰词。self用weak修饰也是因为block 用了copy,为了弱化强引用,我没有答出来主要是以前没有深刻理解这一块的内容。还有关于使用weak不会产生野指针的问题,前两天刚看过这个面试题,面试一问我又忘了,也是因为没有深刻理解,光死记硬背是不行的。

3.Category、扩展、继承的区别,有哪些应用场景,往Category里可以添加属性吗?重写一个类的方法用继承好还是分类好?为什么?如果Category重写父类的方法,执行Category的方法后还会执行父类中的方法吗?项目中哪些地方用到了分类?

分类和扩展的区别:
1.分类原则上只能添加方法(能添加属性的的原因只是通过runtime解决无setter/getter的问题而已)
2.类扩展不仅可以增加方法,还可以增加实例变量(或者属性)
3.类扩展中声明的方法没被实现,编译器会报警告,但是分类中的方法没被实现编译器是不会有任何警告的。这是因为类扩展是在编译阶段被添加到类中,而分类是在运行时添加到类中。
4.类扩展不能像分类那样拥有独立的实现部分(@implementation部分),也就是说,类扩展所声明的方法必须依托对应类的实现部分来实现。
5..m文件里的定义属性,有时我们不会在.h里写属性,因为那样会变成public,只要import后,外部都可以访问。如果我们只想当前类用一下,只需要写在.m里面,这样的属性或方法其实也是扩展的一种。

分类与继承的区别:
分类只能添加新的方法,不能删除修改。
继承可以增加、修改、删除方法。并且可以增加属性。

继承可以修改父类的已经存在方法,比如初始化方法init,重写它之后你可以得到你想要的初始化对象,而分类则可以扩展原有类的方法,比如你想让UIImageView可以从网络设置其image对象,则可以写一个分类,扩展这个原类中没有的方法(或者说功能),因此看你的需求是什么,而不是笼统的说谁好谁坏。

Category重写父类中的方法,会覆盖父类方法,父类方法不会执行。
继承重写一个方法,不会影响到其他地方正常使用这个方法。

项目中用到分类的地方:修改系统分类的样式、功能,比如为NSDate新增分类,增加新的计时方法,为UIColor新增分类,增加新的颜色显示方法。

总结:这道题也是前两天看过的面试题,结果面试时又把Category、扩展、继承三者说混了,后来我发现我的项目中用的都是Category,没有用扩展,因为用了runtime的方法给Category添加属性。这也使得我没有真正理解分类和扩展的区别。现在记住了他们的区别以后可以尝试在项目中多用一些扩展,而不是使用运行时的方式。

4.闭环分享是做什么用的?

答:"闭环分享=分享出去+拉新回来"就是通过用户的社交分享行为引导用户下载App(对于未安装App的用户),或是在App内观看(对于已安装此App的用户),让所有的用户都能直达分享内容所对应的App页面,成功把新用户沉淀下去。

总结:这个是我简历上写的内容,面试官问我这个功能是干什么用的,我答了分享之后再回到app,面试官问我分享完本来就可以回到app,是我描述的不够准确。应该分为未安装app和已安装app的用户分别说明,引导未安装app的用户下载app,已经安装app的用户在app内查看。这个是一年之前做的一个需求,要求分享app上的一个页面到微信,微信上是h5页面,要求点击h5页面上的按钮可以跳回到app查看。当初研究Universal Link的配置还花了很长时间,结果今天问我我什么都没说出来,让别人以为不是我做的一样。

5.在Xib中一个ScrollView,如果内容特别多应该怎么设置约束?

答:在ScrollView中增加一个空白的View。

总结:这个是面试官给的答案,面试官问我如果内容特别多超出一屏怎么让页面滚动,我想不起来直接向她请教了。后来我看了一下我的项目中,我确实是这么做的,自己做过的东西就是想不起来,只能怪自己下的功夫还是不到位。

6.WKWebView和前端如何进行交互,如何改变页面的高度,如何设置页面的背景色,用到的语句是什么,在哪个方法里调用?

WKWebView高度自适应有三种方式:

1.通过KVO的方式

这种方式获取的高度较为准确,但要注意表格中多次回调高度的问题。

添加监听者

#pragma mark ------ < Private Method > ------
#pragma mark
- (void)addWebViewObserver {
    [self.wkWebView.scrollView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:nil];
}

监听高度变化

#pragma mark ------ < KVO > ------
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
        /**  < 法2 >  */
        /**  < loading:防止滚动一直刷新,出现闪屏 >  */
        if ([keyPath isEqualToString:@"contentSize"]) {
            CGRect webFrame = self.wkWebView.frame;
            webFrame.size.height = self.wkWebView.scrollView.contentSize.height;
            self.wkWebView.frame = webFrame;
            [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:[NSIndexPath indexPathForRow:3 inSection:0], nil] withRowAnimation:UITableViewRowAnimationNone];
    }
}

移除观察者

- (void)removeWebViewObserver {
    [self.wkWebView.scrollView removeObserver:self forKeyPath:@"contentSize"];
}

2.通过代理的方式

这种方法通过WKNavigationDelegate代理方法- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation,网页加载完成通过JS获取网页内容高度,但这种方式不一定就是最真实的高度,这时候可能网页内容还未加载完成,但以实际情况为准。

- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    [webView evaluateJavaScript:@"document.body.offsetHeight" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
       CGFloat documentHeight = [result doubleValue];
        CGRect webFrame = webView.frame;
        webFrame.size.height = documentHeight;
        webView.frame = webFrame;
        [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:[NSIndexPath indexPathForRow:3 inSection:0], nil] withRowAnimation:UITableViewRowAnimationNone];
    }];
}

3.通过注入JS的方式,添加网页加载完成回调获取

第三种通常是接口返回HTMLString,然后自己在APP客户端成网页html、head、body这些标签,在合适的位置加入以下js代码:


WKWebView背景色设置方法:

加载前直接给WKWebView.scrollview设背景色 加载完成方法里写设置背景色的js交互方法:

- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    [webView evaluateJavaScript:@"document.body.style.backgroundColor = \"#1E1E1E\"" completionHandler:nil];
}

总结:这些方法的名字我都没记住,开发时用的都是现成的方法,离开代码就想不起来了,以后要锻炼自己手写代码的能力。

7.帧动画图片占用内存过大时如何解决?

答:1.创建图片数组的时候用imageWithContentsOfFile而不是imageNamed方法。

imageNamed:加载到缓存中即使指向他的指针被销毁了,内存中依然会存在。好处是能快速加载该图片,如果这个图片经常被用到建议此方法。

imageWithContentsOfFile:图片是不会缓存的,如果指向他的指针被销毁,内存也会被释放。这个方法适合不经常使用,或者数量多的图片。序列帧的图片非常适合使用这个方法。

2.self.imageView.animationImages = nil;//这句话必须不能少,否则图片会释放不掉的

总结:这道题我按照我平时项目中的做法答的,使用的是一个第三方框架,而面试官主要想知道的是如何解决帧动画图片占用内存的问题,我的回答所答非所问了。

8.TableView嵌套NSTimer卡顿问题如何解决?

答:当你拖动UItableView时,tableView的run loop处于UIEventTrackingRunLoopMode模式,在这种模式下,不处理定时器事件

因此将NSTimer所在的NSRunLoop修改mode为UITrackingRunLoopMode模式或NSRunLoopCommonModes

[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

总结:这道题我只回答了用runloop的方法解决,没答出来具体的方法,这个例子在平时开发中也很常见,应该把这几个方法记住。

9.Vue传值都有什么方法?

1.父组件向子组件传值
2.子组件向父组件传值
3.非父子组件传值

引用官网一句话:父子组件的关系可以总结为 prop 向下传递,事件向上传递。父组件通过 prop 给子组件下发数据,子组件通过事件给父组件发送消息。

总结:今天问了我Vue的东西,因为我的简历上写了,但是前端的面试题还没看,也不会回答,还是先把iOS的面试题弄明白吧。

今日总结:这两天通过面试题学到了不少知识,收获很大,找工作的同时也是深化基础知识的机会,对以后开发很有帮助。我希望通过这一段时间的面试把iOS的基础知识好好过一遍,边找工作边学习,这两天面试答的不是太好,不过面试机会还有很多,希望我每一天的面试都有进步。

你可能感兴趣的:(2021iOS面试题总结day2)