另外一大厂面试的面试题记录,具体哪个就不说了,且看题目分解。
Q :UIButton的父类是什么,往上的继承关系是如何的?
A :解析,这个题主要是考察事件相关的控件即事件传递与响应,UIButton继承自UIControl,UIView,UIResponer,NSObject。当屏幕发生点击事件的时候,事件会由父视图向子视图传递,即通过倒序,深度优先遍历的方式传递,主要的方法是hit-test,在找到第一响应者后如果不能响应事件则通过响应链向父级的方向查找响应者(nextResponser),在找到响应者后优先向手势识别器发送消息,如果有手势识别器处理,则调用touchCancel的方法,否则调用touch方法。详细的内容可参考其他文章查看手势相关具体流程。
Q :UITableView的代理方法中必须实现的是哪些
A :UITableView有两个代理,delegate和dataSource
-
dataSource
dataSource中的代理方法,@required的即必须实现的
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
其他的为@optioned,非必须实现的
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; // Default is 1 if not implemented
//Editing相关的
- (**BOOL**)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath;
...
- delegate
- delegate中的代理方法都是@optioned的,即非必须实现
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
...
heightForRowAtIndexPath是非必要实现的,如果不实现这个方法,默认cell的高度为44.0,(通过cell.frame.size.height打印获取)。
Q :子类中调用[super class]的问题
@implementation Student : Person
- (instance)init{
if([super init]){
NSLog(@"%@",[self class]);
NSLog(@"%@",[super class]);
}
}
A :该题有这几个关键点,1.class方法 2.self 3.super关键字。
首先,class方法是NSObject中定义的实例方法,作用是用来获取示例对象的类对象。
self是隐藏参数,方法调用的时候其内部实现里会转换为objc_msgSend调用,该函数有两个隐藏参数,self和_cmd,即当前对象示例和该方法的方法名,这也就是在方法内部能调用到self的原因,所以,[self class]即获取当前实例的类对象,打印结果,Student。
supe,与self不同,super不是隐藏参数,而是一个标识符,[super class]底层是通过objc_msgSendSuper2实现的,通过中间代(转成C++的编译代码,虽然该形式不是最终的实现,但是有一定参考意义)码可知,该函数需要两个参数,objc_Super2(结构体)、SEL,很明显,SEL是具体的调用,而objc_Super2中包含了一个reciever,即消息接收者,[super class]函数在调用的时候,self传的是当前类即Student的实例,所以消息传递的接收者为Student,所以,打印结果 Student
Q :输出结果
int a[5] = {16, 25, 36, 44, 53};
int *ptr = (int *)(&a + 1);
printf("%d, %d", *(a + 1), *(ptr - 1));
A :
-
int a[5] = {16, 25, 36, 44, 53};:定义了一个包含5个整数的数组a,并初始化其元素。 -
int *ptr = (int *)(&a + 1);:这行代码的目的是将指针ptr指向数组a后的一个位置。&a获取数组a的地址,然后+ 1将指针移动到数组的下一个位置,然后将其类型转换为int*。 -
printf("%d, %d", *(a + 1), *(ptr - 1));:这行代码使用两次printf打印两个值。*(a + 1):这里是数组索引操作,表示访问数组a的第2个元素(索引为1)。即,输出数组中的第2个元素25。*(ptr - 1):这里是对指针进行操作,ptr指向数组a后的一个位置,ptr - 1表示移动回数组a最后一个元素的位置。即,输出数组中的最后一个元素53。
因此,代码输出为 25, 53。
Q :
- (void)viewDidLoad{
[super viewDidLoad];
NSLog(@"1");
dispatch_sync(dispatch_main_queue(),^(){
NSLog(@"2");
});
NSLog(@"3");
}
A : 在当前线程的队列中同步添加任务会造成死锁,原因就是sync会阻塞线程,执行NSLog(@"2"),
造成了当前线程(主线程)等待dispatch_sync中任务完成,而dispatch_sync中任务又在等当前线程中任务完成才执行(即队列前面的任务)。
Q :
NSMutableString *str1 = [[NSMutableString alloc]initWithString:@"name"];
NSLog(@"str1 = %p",str1);
NSMutableString *copyStr = [str1 copy];
NSLog(@"copyStr = %p",copyStr);
[copyStr appendString:@"123"];
A :str1 和 copyStr 的地址不一样,最后一句会crash。
- 可变类型进行copy操作,深拷贝。
- 深拷贝的结果是不可变对象,即copyStr是NSString类型,所以不能进行appendString操作。
拓展
| 标题 | copy | mutableCopy |
|---|---|---|
| NSString (NSArray、NSDictionary) | 浅拷贝(拷贝后对象不可变) | 深拷贝(拷贝后对象可变) |
| MutableString (NSMutableArray、NSMutableDictionary) | 深拷贝(拷贝后对象不可变) | 深拷贝(拷贝后对象可变) |
Q : 算法题,你正在爬楼梯。需要 n 步你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
A :
1. 递归算法:
function climbStairsRecursive(n) {
if (n <= 1) {
return 1;
}
return climbStairsRecursive(n - 1) + climbStairsRecursive(n - 2);
}
console.log(climbStairsRecursive(5)); // 输出爬到第 5 阶的方法数
2. 动态规划算法:
function climbStairsDP(n) {
if (n <= 1) {
return 1;
}
const dp = new Array(n + 1);
dp[0] = 1;
dp[1] = 1;
for (let i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
console.log(climbStairsDP(5)); // 输出爬到第 5 阶的方法数
在动态规划算法中,使用一个数组
dp来保存到达每一步的方法数。状态转移方程为dp[i] = dp[i - 1] + dp[i - 2],其中dp[i]表示到达第i阶的方法数。 动态规划算法的效率更高,因为它避免了重复计算,而递归算法可能会重复计算相同的子问题。