iOS开发 基础一
1.UIView和CALayer之间的关系
- UIView继承自
UIResponder
可响应用户事件,CALayer不可以响应用户事件 - UIView真正的绘图部分由CALayer实现。
2.请输出C语言执行结果
#include<stdio.h>
int main(){
int a[5] = {1,2,3,4,5};
int *ptr = (int *)(&a + 1);
printf("%d,%d,%d \n",*(a+1),*(ptr-1),*(ptr));
}
- 输出的结果2,5
- &a是整个数组的首地址,而a是数组首元素的首地址.
- &a+1就是相当于整个数组指针加1,执行&a+1后,ptr的偏移量相当于 a + sizeof(int) * 5 * 1
3.请指出下面这段代码问题
for (int i = 0; i < 10000; i++ ) {
NSString *str = @"ABC";
str = [str lowercaseString];
str = [str stringByAppendingString:@"xyz"];
NSLog(@"%@",str);
}
- 内存得不到及时地释放,内存暴涨.RunLoop是在每个事件循环结束后才会自动释放池去使对象的引用计数减一,对于引用计数为0的对象才会真正被销毁、回收内存。正确的写法如下
for ( int i = 0 ; i < 10000 ; ++ i ) {
@ autoreleasepool {
NSString *str = @"Abc" ;
str = [ str lowercaseString ] ;
str = [ str stringByAppendingString : @"xyz" ] ;
NSLog ( @"%@" , str ) ;
}
}
4.APP的启动优化
优化的时候会将优化以main()
函数为界分为2个部分,即main之前的pre-main
阶段 和 main()
之后。
pre-main
阶段这个阶段是由dyld(动态连接器)
来操作的,设置DYLD_PRINT_STATISTICS
进行检测这个阶段耗时
-
dylib loading time: 动态库加载耗时(169.23ms)。关于动态库的加载,这个是不可避免的,我们能做的就是减少动态库的引用,官方的建议的是动态库的使用应该在6个以内,所以这里就引入了一个动态库合并的概念,通过合并动态库,从而减少在pre-main时的加载时间。
-
rebase/binding: 偏移修正/符号绑定。这个过程由操作系统完成。(ASLR安全机制,在二进制文件头部添加随机值)/
-
ObjC setup: OC类注册。这也就意味着项目中OC类越多,这里消耗的时间也就会增加。
-
initializer: 这个阶段指的是+ (void)load,C++构造函数等初始化操作。 这里可以看到用时5.1 seconds,是所有项做高的。这里是因为我在项目里面随便的一个类里实现了+(void)load函数,并模拟了一个耗时操作。所以这里的优化比较明确:1. 能不使用+load就尽量不要使用,可以将load内部逻辑推迟到initialize时;2. 使用到了load,就尽量不要在内部执行耗时操作;3. 如果混编了C++代码,要尽量减少构造函数中的耗时操作
-
slowest intializers: 启动时用时最慢的文件,这个可以看到耗时最多的是TestApp项目本身,这里主要是由于那个模拟的耗时操作导致。
在main()
函数之后的优化就因项目不同而异了,大致有这么几个核心:
- 业务逻辑:这里主要指APP从启动到首页呈现的阶段。尽量减少与该阶段无关且没有必要的初始化代码操作,把这部分代码以懒加载的方式处理。
- 删除无用代码:这里是随着业务的发展,APP不断的迭代更新,会产生很多的的下架业务,从而堆积了很多的无用代码,这些代码会增加ObjC setup的耗时,所以要清理掉。
- 多线程操作:在启动时,将一些必要的非UI业务且需要初始化操作的任务放在子线程中,这样可以在APP启动的时候,发挥CPU的最大性能。
- 启动页面:首要呈现的画面,尽量减少使用 .xib 或storyBoard来实现,因为它们需要解析成代码,会造成耗时。
- 业务具体优化:提升代码质量,采用最合理的实现方式等。
5.多个线程访问同一个对象、同一个变量、同一个文件如何保证线程安全
(1)添加互斥锁synchronized
- 互斥锁使用格式
@synchronized(锁对象) { // 需要锁定的代码 }
- 优点:能有效防止因多线程抢夺资源造成的数据安全问题
- 缺点:需要消耗大量的CPU资源
- 互斥锁的使用前提:多条线程抢夺同一块资源
(2)在GCD中使用semaphore进行加锁
/**
* 售卖火车票(线程安全)
*/
- (void)saleTicketSafe {
while (1) {
// 相当于加锁
dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);
if (self.ticketSurplusCount > 0) { // 如果还有票,继续售卖
self.ticketSurplusCount--;
NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]);
[NSThread sleepForTimeInterval:0.2];
} else { // 如果已卖完,关闭售票窗口
NSLog(@"所有火车票均已售完");
// 相当于解锁
dispatch_semaphore_signal(semaphoreLock);
break;
}
// 相当于解锁
dispatch_semaphore_signal(semaphoreLock);
}
}
文章参考https://www.jianshu.com/p/2d57c72016c6
https://www.cnblogs.com/wendingding/p/3805841.html
6.什么是线程死锁,什么情况下会造成线程死锁
- 是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
(1)GCD主队类中同步添加任务
- (void)syncMain{
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"task1-%@",[NSThread currentThread]);
dispatch_sync(queue, ^{
NSLog(@"task2-%@",[NSThread currentThread]);
});
NSLog(@"task3-%@",[NSThread currentThread]);
}
原因:task1是在主线程中执行,而主线程是串行队列,定义的queue队列也是主队列, dispatch_sync是同步执行的标志,意思是必须等待block返回,才能执行task3,而当前主队列中正在被task1执行,必须等待完成task3完成后才能释放,这就造成了task3等待block完成返回,block等待task3完成释放主队列而相互等待的循环中死锁。
(2)串行队列异步&同步
- (void)gcdTest{
dispatch_queue_t queue = dispatch_queue_create("com.demo.serialQueue", DISPATCH_QUEUE_SERIAL);
NSLog(@"task1-%@",[NSThread currentThread]);
dispatch_async(queue, ^{
NSLog(@"task2-%@",[NSThread currentThread]);
dispatch_sync(queue, ^{
NSLog(@"task3-%@",[NSThread currentThread]);
});
NSLog(@"task4-%@",[NSThread currentThread]);
});
NSLog(@"task5-%@",[NSThread currentThread]);
}
输出结果为test1 - test5 - tesk2
原因:task2、task4与task3在同一队列中执行,dispatch_sync确定了task4需要等待task3完成后返回才能执行,而task2任务执行的时候已经占用了当前队列,需要等到task4完成后才能释放,这就造成了task3等待task4完成,task4等待task3返回的相互等待,这也是队列阻塞造成的死锁。
7.TCP的一些知识点
8.HTTPS和HTTP的区别
* https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
* http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
* http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
* http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
9.GCD信号量-dispatch_semaphore_t的作用
- 并发队列 + 异步添加任务 + 同步执行
- 并发的网络请求,等待网络1请求完成之后,在进行网络2请求
- 并发网络请求,等待最后一个网络请求完成后刷新UI
- 多线程多任务刷新向数组中添加对象,防止内存崩溃问题