一、选择题(每题5分)
LGTeacher继承于LGPerson, 下面代码输出为什么 () 分值5分
LGTeacher *t = [[LGTeacher alloc] init];
- (instancetype)init{
self = [super init];
if (self) {
NSLog(@"%@ - %@",[self class],[super class]);
}
return self;
}
复制代码
- [☑️] A: LGTeacher - LGTeacher
- B: LGTeacher - LGPerson
- C: LGTeacher - NSObject
- D: LGTeacher - 它爱输出啥就输出啥,我不清楚
- 下面代码能否正常执行以及输出 () 分值5分
@interface LGPerson : NSObject
@property (nonatomic, retain) NSString *kc_name;
- (void)saySomething;
@end
@implementation LGPerson
- (void)saySomething{
NSLog(@"%s - %@",__func__,self.kc_name);
}
@end
- (void)viewDidLoad {
[super viewDidLoad] ;
Class cls = [LGPerson class];
void *kc = &cls;
[(__bridge id)kc saySomething];
}
复制代码
- A: 能 - ViewController
- B: 能 - null
- [☑️] C: 能 - ViewController: 0x7ff8d240ad30
- D: 能不能自己运行一下不就知道,非要问我 - 它爱输出啥就输出啥,我不清楚
- 下面代码执行,控制台输出结果是什么 () 分值5分
NSObject *objc = [NSObject new];
NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)(objc)));
void(^block1)(void) = ^{
NSLog(@"---%ld",CFGetRetainCount((__bridge CFTypeRef)(objc)));
};
block1();
void(^__weak block2)(void) = ^{
NSLog(@"---%ld",CFGetRetainCount((__bridge CFTypeRef)(objc)));
};
block2();
void(^block3)(void) = [block2 copy];
block3();
__block NSObject *obj = [NSObject new];
void(^block4)(void) = ^{
NSLog(@"---%ld",CFGetRetainCount((__bridge CFTypeRef)(obj)));
};
block4();
复制代码
- A: 1 2 2 2 2
- B: 1 2 3 3 2
- C: 1 3 3 4 1
- [☑️] D: 1 3 4 5 1
- 下面代码执行,控制台输出结果是什么 () 分值5分
- (void)MTDemo{
while (self.num < 5) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
self.num++;
});
}
NSLog(@"KC : %d",self.num);
}
- (void)KSDemo{
for (int i= 0; i<10000; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
self.num++;
});
}
NSLog(@"Cooci : %d",self.num);
}
复制代码
- A: 0 , 10000
- B: 0 , <10000
- C: <=5 , <10000
- [☑️] D: >=5 , <10000
- 下面代码执行,控制台输出结果是什么 () 分值5分
- (void)textDemo2{
dispatch_queue_t queue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"1");
dispatch_async(queue, ^{
NSLog(@"2");
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
- (void)textDemo1{
dispatch_queue_t queue = dispatch_queue_create("cooci", NULL);
NSLog(@"1");
dispatch_async(queue, ^{
NSLog(@"2");
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
复制代码
- [☑️] A: 1 5 2 3 4 , 1 5 2
- B: 1 5 2 4 3 , 死锁奔溃
- C: 1 5 2 3 4 , 死锁奔溃
- D: 1 5 2 3 , 死锁奔溃
- 下面代码执行,控制台输出结果是什么 () 分值5分
@property (nonatomic, strong) NSMutableArray *mArray;
- (NSMutableArray *)mArray{
if (!_mArray) _mArray = [NSMutableArray arrayWithCapacity:1];
return _mArray;
}
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *arr = [NSMutableArray arrayWithObjects:@"1",@"2", nil];
self.mArray = arr;
void (^kcBlock)(void) = ^{
[arr addObject:@"3"];
[self.mArray addObject:@"a"];
NSLog(@"KC %@",arr);
NSLog(@"Cooci: %@",self.mArray);
};
[arr addObject:@"4"];
[self.mArray addObject:@"5"];
arr = nil;
self.mArray = nil;
kcBlock();
}
复制代码
- A: 1 2 4 5 3 , nil
- [☑️] B: 1 2 4 5 3 , a
- C: 1 2 4 5 3 , 1 2 4 5 3 a
- D: 1 2 4 5 3 a , 1 2 4 5 3 a
二、判断题 (每题5分)
- 可变数组线程是安全 () 分值5分
- 对
- 错 ☑️
- 主队列搭配同步函数就会产生死锁 () 分值5分
- 对
- 错 ☑️
- 下面代码执行不会报错 () 分值5分
int a = 0;
void(^ __weak weakBlock)(void) = ^{
NSLog(@"-----%d", a);
};
struct _LGBlock *blc = (__bridge struct _LGBlock *)weakBlock;
id __strong strongBlock = [weakBlock copy];
blc->invoke = nil;
void(^strongBlock1)(void) = strongBlock;
strongBlock1();
- 对 ☑️
- 错
- 下面代码执行不会报错 () 分值5分
NSObject *a = [NSObject alloc];
void(^__weak block1)(void) = nil;
{
void(^block2)(void) = ^{
NSLog(@"---%@", a);
};
block1 = block2;
NSLog(@"1 - %@ - %@",block1,block2);
}
block1();
- 对
- 错 ☑️
- 下面代码会产生循环引用 () 分值5分
__weak typeof(self) weakSelf = self;
self.doWork = ^{
__strong typeof(self) strongSelf = weakSelf;
weakSelf.doStudent = ^{
NSLog(@"%@", strongSelf);
};
weakSelf.doStudent();
};
self.doWork();
- 对 ☑️
- 错
- 下面代码是否有问题 () 分值5分
- (void)demo3{
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(0, 0);
for (int i = 0; i<5000; i++) {
dispatch_async(concurrentQueue, ^{
NSString *imageName = [NSString stringWithFormat:@"%d.jpg", (i % 10)];
NSURL *url = [[NSBundle mainBundle] URLForResource:imageName withExtension:nil];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
dispatch_barrier_async(concurrentQueue, ^{
[self.mArray addObject:image];
});
});
}
}
- 对 ☑️ 有问题
- 错
- 下面代码不会产生循环引用 () 分值5分
static ViewController *staticSelf_;
- (void)blockWeak_static {
__weak typeof(self) weakSelf = self;
staticSelf_ = weakSelf;
}
复制代码
- 对
- 错 ☑️ staticSelf_ 是全局静态变量导致self无法被释放
三、简单题 (每题 10分 合计 120分)
请把它当成一场面试,认真对待 希望大家耐心 切忌浮躁 (和谐学习 不急不躁)
-
1、请用GCD实现读写锁 , 解释为什么这么设计 分值10分 读写锁需要具有一下特点
- 同一时间,只有一个线程进行写的操作
- 同一时间,允许多个线程进行读的操作
- 同一时间,不许即有写又有读的操作
#import "ViewController.h"
@interface ViewController ()
@property (strong, nonatomic) dispatch_queue_t queue;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.queue = dispatch_queue_create("rw_queue", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 10; i++) {
[self read];
[self write];
}
}
- (void)read {
dispatch_async(self.queue, ^{
sleep(1);
NSLog(@"read");
});
}
- (void)write
{
dispatch_barrier_async(self.queue, ^{
sleep(1);
NSLog(@"write");
});
}
@end
-
dispatch_barrier_async 栅栏函数,之后的异步函数需要等到栅栏函数执行完成后才能执行。
-
2、
@synchronized为什么应用频次最多 分值10分- 底层封装了一把递归锁,可以自动进行加锁解锁
- lockCount控制递归,threadCount控制多线程,可以进行递归加锁
- 写法简单
-
3、
block的种类有几种 是符合区分的? 分值10分- globalBlock : 全局block,在block内部不使用外部变量,或者使用静态变量和全局变量
- mallocBlock :堆Block,在block内部使用OC变量,并且赋值给强引用或者copy修饰的的对象
- stackBlaock :栈block,在block内部使用了局部变量或者oc属性,但是不能赋值给强引用或者copy修饰的对象
-
4、
KVC普通对象setter过程 分值10分- 1.查找调用setKey:方法,没有则查找调用_setKey:没有则调用第三步setIsKey
- 2.三个个方法都没有就查找 accessInstanceVariableDirecity 看返回值是Yes还是No
- 3.Yes就按顺序查找是否有 _key _iskey key isKey 这些成员变量 有就给他们赋值
- 4.No 或者 没有对应的成员变量就报错说没有这样的成员变量
-
5、
KVO底层原理机制分析 分值10分- 基于runtime机制实现,系统会在运行时动态创建一个派生类,原来类的isa指针指向了这个派生类,并且这个排成类中重写了被观察属性的setter方法,并且派生类中会调用willChangeValueForKey来记录旧值和didChangeValueForKey通知值发生来辩
-
6、下面代码的栈帧入栈情况是什么的? 分值10分
- (void)viewDidLoad {
[super viewDidLoad] ;
Class cls = [LGPersonP class];
void *kc = &cls;
[(__bridge id)kc saySomething];
LGPersonP *person = [LGPersonP alloc];
}
-
self对象-> cmd(viewDidLoad)-> self 类入栈 -> self对象 -> LGPerson类入栈 -> person对象入栈
-
7、iOS 线程如何保活, 为什么要线程保活 分值10分
1.创建子线程
self.thread = [[XMGThread alloc] initWithTarget:self selector:@selector(run) object:nil];
self.thread.name = @"开启的常驻线程";
[self.thread start];
- (void)run {
NSLog(@"----------run----%@",
[NSThread currentThread]);
//1.方法1 addPort
[[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
}
线程不死:经常要在后台(子线程)做耗时操作 如:==在后台监控用户的操作==
-
8、循环引用,为什么要在
block中加strong,不加会怎样 分值10分- 通常使用__weak typeof(self) weakSelf = self, 利用弱引用来姐姐循环引用的问题,但是可能会存在过早释放的问题
- 比如block内部有一个延时处理,加入__strong typeof(weakSelf)strongDelg = weakSelf;延长了生命周期,
-
9、你使用过
dispatch_once吗?了解底层吗?让你实现一个应该怎么操作? 分值10分- 进入 dispatch_once_f val传值是onceToken的静态变量,
- 将val转为dispatch_once_gate_t类型的变量
- 通过os_atomic_load获取这个变量中的v标志
- 如果这个标志 为DLOCK_ONCE_DONE 则表示这个是已经执行过的单例直接返回。
-
10、iOS 多线程原理和线程生命周期是什么样的 分值10分
- 原理: cup在多个任务直接进行快速切换,因为时间足够块显示在同时执行的效果。
- 生命周期
- 任何任务都需要在线程中才能完成
- 1.判断是否有空闲的线程,在判断线程数量是否超过阈值。
- 2.创建线程New
- 3.就绪runnable
- 4.CPU调度当前线程就Running
- 5.CPU调度其他线程就进入等待 Blocked
- 6.重新进入就绪状态
- 7.调用完成后死亡。
-
11、请简述信号量和调度组的原理 分值10分
- 信号量作用一般是用来使任务重视执行,类似互斥锁,控制GCD的最大并发数
- dispatch_semaphore_create 创建信号量
- dispatch_semaphore_wait 信号等待 底层是 -1操作,小于0 等待,0或大于0则继续
- dispatch_semaphore_signal 信号量释放 value>0则重置为0,正常执行,如果加1后还是小于0,报出异常,
dispatch_semaphore_wait操作过多,则进入_dispatch_semaphore_signal_slow
作用:控制任务执行顺序 dispatch_group_create 创建组 dispatch_group_async 进组任务 dispatch_group_notify 进组任务执行完毕通知 dispatch_group_wait 进组任务执行等待事件 dispatch_group_enter 进组 dispatch_group_leave 出组 其实底层也是对信号量+-1的操作 0则继续,>0 则等待
-
12、请简述
__block修饰变量被 block 捕获之后的情况 分值10分
__block的作用是生成了一个__Block_byref_a_0结构体让block捕获成员变量的地址值,而不是简简单单的值,放block不会变量的地址值。完成修改同一个内存地址的作用
四、拓展满分题 (15分)
- 作为一名iOS中高级开发人员, 你的加分项和优势是什么?