iOS开发 基础一

1.UIView和CALayer之间的关系

  • UIView继承自UIResponder可响应用户事件,CALayer不可以响应用户事件
  • UIView真正的绘图部分由CALayer实现。

2.请输出C语言执行结果

#include
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

文章参考:https://www.cnblogs.com/yr-linux/p/5079897.html

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来实现,因为它们需要解析成代码,会造成耗时。
  • 业务具体优化:提升代码质量,采用最合理的实现方式等。

文章参考 https://www.jianshu.com/p/5b41cb0c70ba

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返回的相互等待,这也是队列阻塞造成的死锁。

参考文章:https://www.cnblogs.com/beckwang0912/p/7146833.html

7.TCP的一些知识点

https://blog.csdn.net/qq_38950316/article/details/81087809

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
  • 多线程多任务刷新向数组中添加对象,防止内存崩溃问题

https://www.jianshu.com/p/24ffa819379c

10.关于AFNet的封装与思考

https://www.jianshu.com/p/d1115244006d

你可能感兴趣的:(iOS开发 基础一)