iOS笔试-上机题(附个人见解)

随机配图

机试题目如下


用命令行创建一个以CocoaPods管理的项目【Test-你的姓名拼音】,新建3个ViewController,完成以下题目
    1. 将下面的问题在一个UITabView里面列出所有问题,单击每一行进入一个新的页面,里面是问题和答案。
      • 1.1 什么是VFL,请说出“H:[_aImageView(==50)]-10-[_aButton]”代表的意思?
      • 1.2 NSPersistentStoreCoordinator\NSManagedObjectContext和NSManagedObject三者什么关系
      • 1.3 ARC下什么时候使用Strong,什么时候使用Weak,如何避免循环引用?
      • 1.4 将一个MRC的项目转换为ARC的项目,应该遵循什么规则?
      • 1.5 Objective-C如何对内存管理的,有哪些情况会导致崩溃,说说你的看法和解决方法?
      • 1.6 简述GCD是如何简化线程编程的?
      • 1.7 Extensions有哪些类型,每种类型可以完成的功能有哪些?
      • 1.8 如何衡量ViewController的规模?
      • 1.9 MVVM是什么,请简述MVVM的特点以及解决了哪些问题?
  • 2.请使用UIScrollerView控件实现图片的循环切换
  • 3.请使用UICollectionView控件实现图片数据绑定,要求每行两列

参考思路

1.新建以CocoaPods管理的项目

  • 在命令行窗口输入以下命令
  • pod init
  • pod install
  • 等待Updating完毕以后文件夹的样式
    文件夹样式
  • 打开test-cyx.xcworkspace文件

PS:下面只写思路,由于有点强迫症,想把题目做的完整一点,时间又不允许,就只把思路写写就好了

2.第一题(创建一个TableViewController)

  • 1.我们假定问题和答案分别是字典的Key和Value,把十道题目分别存放到一个.plist文件中,使之能从.plist文件中读取
  • 2.根据MVC思想,我们面向模型开发,而不是面向字典开发,因此,我们从plist文件中读出的字典数组需要转化为模型数组

    • (1)(M)我们需要定义模型test类,里面有两个属性:
    • @property (nonatomic, copy) NSString *testName;题目
    • @property (nonatomic, copy) NSString *testContext;答案
    • (2)(V)创建一个用于展示问题和答案的PageViewController类,.h头文件里向外暴露了@property (nonatomic, strong) YXTest * text;属性
    • (3)(C)通过TableViewController类里面使用KVC把字典数据转为模型数据,再在以下方法把模型数据传递给PageViewController用于展示。

      1
      2
      3
      4
      5
      - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
      PageViewController *page = [[PageViewController alloc]init];
      page.text = self.text;
      [self.navigationController pushViewController:page animated:YES];
      }

上面的Demo骨架已经搭完了,下面是解析一下问题的答案(当然,这些问题都可以从网上直接找到答案,这里只是简单说说我个人的理解,如有纰漏,欢迎指正)

  • 什么是VFL,请说出“H:[_aImageView(==50)]-10-[_aButton]”代表的意思?

    • VFL:苹果为了简化手写Autolayout代码发明出来的,好像叫可视化格式语言(表示真的很久没用过啊!自从发现了Masonry),那行代码的意思应该是设置水平方向的约束。
    • 手写Autolayout代码我一般使用第三方框架:Masonry。用Masonry写出来的代码的可读性非常好。
  • NSPersistentStoreCoordinator\\\\NSManagedObjectContext和NSManagedObject三者什么关系

    • CoreData里面的属性,NSPersistentStoreCoordinator:持久性数据协调器;NSManagedObjectModel:管理数据模型;NSManagedObjectContext:管理数据内容。三者的关系:CoreData根据NSManagedObjectModel对象确定如何将底层的持久化文件中的数据映射为NSManagedObject对象。
    • 对于数据持久化的操作,我使用比较多的是通过FMDB框架操作SQLite,因为CoreData是基于OC封装了SQLite,性能并没有SQLite好。例如GCD性能比NSOperation好。
  • ARC下什么时候使用Strong,什么时候使用Weak,如何避免循环引用?

    • ARC下,是Xcode编译器自动判断是否有强指针引用着对象,从而自动帮我们在恰当的位置加上引用计数加一或减一的代码。使用Strong就表明这个OC对象是被使用强指针引用的。
    • 因此,我们自己定义的需要使用的OC属性(UI控件除外)时,一般使用Strong;UI控件一般使用weak,因为在UI控件通常被父控件的subViews数组强引用着。
    • 首先,循环引用的意思是两个对象互相强引用着(或者多个对象引用循环),造成互相都无法释放,效果类似与‘死锁’。避免循环引用的方式是将其中一个对象设置为weak。我印象比较深的在使用block时造成的循环引用,例如使用AFN的时候
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      // 在AFN的block内使用,防止造成循环引用
      __weak typeof(self) weakSelf = self;
      [[AFHTTPSessionManager manager] GET:CYXRequestURL parameters:params success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) {
      NSLog(@"请求成功");
      // 利用MJExtension框架进行字典转模型
      weakSelf.menus = [CYXMenu objectArrayWithKeyValuesArray:responseObject[@"result"]];
      // 刷新数据(若不刷新数据会显示不出)
      [weakSelf.tableView reloadData];
      } failure:^(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error) {
      NSLog(@"请求失败 原因:%@",error);
      }];
  • 将一个MRC的项目转换为ARC的项目,应该遵循什么规则?

    • (我也不太确定,猜的)规则难道是,需要转换为ARC文件的就转换,,不需要转换的就不转换?过滤掉无需转换的文件(不支持ARC的文件)。无需转换的文件应添加-fno-objc-arc标记
  • Objective-C如何对内存管理的,有哪些情况会导致崩溃,说说你的看法和解决方法?

    • (…上面已经问过ARC了,这题我猜是问iOS系统的内存管理原则了吧?)
    • 当App收到三次内存警告还不做处理时,会造成闪退。
    • 处理方法:在didReceiveMemoryWarning内释放不必要的资源。
  • 简述GCD是如何简化线程编程的?

    • GCD相对于pthread/NSThread,通过自动管理线程的生命周期,从而简化了线程编程。
  • Extensions有哪些类型,每种类型可以完成的功能有哪些?

    • (那时候有几个忘了….)六种类型:Today、Share、Action、Photo Editing、Storage Provider、Custom keyboard
    • 完成的功能可以顾名思义
  • 如何衡量ViewController的规模?
    -(这个真的不太清楚。。求大神指导)是代码量?业务逻辑的复杂程度?还是ViewController做了过多数据加工的事情,造成ViewController的规模变大?

  • MVVM是什么,请简述MVVM的特点以及解决了哪些问题

    • M(Model)V(View)VM(ViewModel),是一种View层的架构模式,衍生自MVC。
    • 特点:把数据加工的任务从Controller中移到了ViewModel,使得Controller只需要专注于数据调配的工作,ViewModel则去负责数据加工并通过通知机制让View响应ViewModel的改变。
    • 目标:为MVC中的Controller减负

请使用UIScrollerView控件实现图片的循环切换

- CYXInfiniteScrollView.h文件

1
2
3
4
5
6
7
#import <UIKit/UIKit.h>
@interface CYXInfiniteScrollView : UIView
@property (strong, nonatomic) NSArray *images;
@property (weak, nonatomic, readonly) UIPageControl *pageControl;
@property (assign, nonatomic, getter=isScrollDirectionPortrait) BOOL scrollDirectionPortrait;
@end

- CYXInfiniteScrollView.m文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#import "CYXInfiniteScrollView.h"
static int const ImageViewCount = 3;
@interface CYXInfiniteScrollView() <UIScrollViewDelegate>
@property (weak, nonatomic) UIScrollView *scrollView;
@end
@implementation CYXInfiniteScrollView
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
// 滚动视图
UIScrollView *scrollView = [[UIScrollView alloc] init];
scrollView.showsHorizontalScrollIndicator = NO;
scrollView.showsVerticalScrollIndicator = NO;
scrollView.pagingEnabled = YES;
scrollView.bounces = NO;
scrollView.delegate = self;
[self addSubview:scrollView];
self.scrollView = scrollView;
// 图片控件
for (int i = 0; i<ImageViewCount; i++) {
UIImageView *imageView = [[UIImageView alloc] init];
[scrollView addSubview:imageView];
}
// 页码视图
UIPageControl *pageControl = [[UIPageControl alloc] init];
[self addSubview:pageControl];
_pageControl = pageControl;
}
return self;
}
- (void)layoutSubviews
{
[super layoutSubviews];
self.scrollView.frame = self.bounds;
if (self.isScrollDirectionPortrait) {
self.scrollView.contentSize = CGSizeMake(0, ImageViewCount * self.bounds.size.height);
} else {
self.scrollView.contentSize = CGSizeMake(ImageViewCount * self.bounds.size.width, 0);
}
for (int i = 0; i<ImageViewCount; i++) {
UIImageView *imageView = self.scrollView.subviews[i];
if (self.isScrollDirectionPortrait) {
imageView.frame = CGRectMake(0, i * self.scrollView.frame.size.height, self.scrollView.frame.size.width, self.scrollView.frame.size.height);
} else {
imageView.frame = CGRectMake(i * self.scrollView.frame.size.width, 0, self.scrollView.frame.size.width, self.scrollView.frame.size.height);
}
}
CGFloat pageW = 100;
CGFloat pageH = 100;
CGFloat pageX = self.scrollView.frame.size.width - pageW;
CGFloat pageY = self.scrollView.frame.size.height - pageH;
self.pageControl.frame = CGRectMake(pageX, pageY, pageW, pageH);
[self updateContent];
}
- (void)setImages:(NSArray *)images
{
_images = images;
// 设置页码
self.pageControl.numberOfPages = images.count;
self.pageControl.currentPage = 0;
// 设置内容
[self updateContent];
}
#pragma mark - <UIScrollViewDelegate>
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
// 找出最中间的那个图片控件
NSInteger page = 0;
CGFloat minDistance = MAXFLOAT;
for (int i = 0; i<self.scrollView.subviews.count; i++) {
UIImageView *imageView = self.scrollView.subviews[i];
CGFloat distance = 0;
if (self.isScrollDirectionPortrait) {
distance = ABS(imageView.frame.origin.y - scrollView.contentOffset.y);
} else {
distance = ABS(imageView.frame.origin.x - scrollView.contentOffset.x);
}
if (distance < minDistance) {
minDistance = distance;
page = imageView.tag;
}
}
self.pageControl.currentPage = page;
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self updateContent];
}
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
[self updateContent];
}
#pragma mark - 内容更新
- (void)updateContent
{
// 设置图片
for (int i = 0; i<self.scrollView.subviews.count; i++) {
UIImageView *imageView = self.scrollView.subviews[i];
NSInteger index = self.pageControl.currentPage;
if (i == 0) {
index--;
} else if (i == 2) {
index++;
}
if (index < 0) {
index = self.pageControl.numberOfPages - 1;
} else if (index >= self.pageControl.numberOfPages) {
index = 0;
}
imageView.tag = index;
imageView.image = self.images[index];
}
// 设置偏移量在中间
if (self.isScrollDirectionPortrait) {
self.scrollView.contentOffset = CGPointMake(0, self.scrollView.frame.size.height);
} else {
self.scrollView.contentOffset = CGPointMake(self.scrollView.frame.size.width, 0);
}
}

请使用UICollectionView控件实现图片数据绑定,要求每行两列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#import "CYXCollectionViewController.h"
@interface CYXCollectionViewController ()
@end
@implementation CYXCollectionViewController
static NSString * const CYXCell = @"cell";
- (instancetype)init
{
// 流水布局
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
layout.itemSize = CGSizeMake(150, 150);
layout.minimumLineSpacing = 0;
layout.minimumInteritemSpacing = 20;
layout.sectionInset = UIEdgeInsetsMake(20, 0, 0, 0);
return [self initWithCollectionViewLayout:layout];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.collectionView.backgroundColor = [UIColor whiteColor];
// 注册cell
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier: CYXCell];
}
#pragma mark - <UICollectionViewDataSource>
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return 30;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier: CYXCell forIndexPath:indexPath];
UIImageView *view = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"xxx"]];
cell.backgroundView = view;
return cell;
}
@end