• 2019-03-27 面试题整理


    1. 属性修饰符

    常用的属性修饰符有

    atomic,nonatomic,strong,retain,weak,assign,unsafe_unretained,copy,readonly,readwrite

    2. ARC下,不指定属性修饰符时,默认的是

    1. 基本数据类型:atomic readwrite assign
    2. 普通OC对象:atomic readwrite strong

    3. 关于copystrong

    可变对象 copy是深拷贝

    不可变对象 copy是浅拷贝

    mutableCopy 始终是深拷贝

    3.1 为什么要用copy修饰NSString/NSArray/NSDictory

    因为使用copy来修饰不可变对象,可以保证安全

    扩展:

    copy浅拷贝 不拷贝对象本身,仅仅是拷贝指向对象的指针(复制的对象和原对象都指向同一个地址)

    mutableCopy深拷贝 直接拷贝整个对象内存到另一块内存中

    3.2 使用copy去修饰NSMutableArray会怎么样?

    使用copy修饰可变数组之后,数组初始化的时候,会执行copy方法,生成的是一个不可变的数组,当执行[arr addObject:]时会crash

    4. atomic是否是绝对线性安全的

    atomic原子性,不是绝对线性安全的

    @property (atomic, assign) NSInteger intA;   //有一个atomic的属性,表示是原子的
     
     
    - (void)viewDidLoad {
       [super viewDidLoad];
       //开启一个线程对intA的值+1
       dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
           for (int i = 0;i < 1000;i ++){
               self.intA = self.intA + 1;
           }
           NSLog(@"intA : %ld",(long)self.intA);
       });
       
       //开启一个线程对intA的值+1
       dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
           for (int i = 0;i < 1000;i ++){
               self.intA = self.intA + 1;
           }
           NSLog(@"intA : %ld",(long)self.intA);
       });   
    }
    
    

    错误分析:
    因为intAatomic修饰的,所以是线程安全的,在+1的时候,只会有一个线程去操作,所以最终的打印结果必定有一个是2000

    输出如下:

    intA : 1186
    intA : 896
    

    分析:
    其实atomic是原子的是没问题的,这个只是表示set方法是原子的,效果类似与下面的效果

    
    //atomic表示的是对set方法加锁,表示在设置值的时候,只会有一个线程执行set方法
    - (void)setIntA:(NSInteger)intA{
        [self.lock lock];
        _intA = intA;
        [self.lock unlock];
    }
    

    只是对set方法加锁,而我们代码里面的self.intA = self.intA + 1;,这一部分不是线程安全的,正确的处理方法是:

    [self.lock lock];
    self.intA = self.intA + 1;
    [self.lock unlock];
    

    5. 进程与线程,堆和栈

    一个程序至少有一个进程,一个进程至少有一个线程。同一个进程内的线程共享进程里的资源。

    堆 由程序员分配释放,一般用来存放对象(ARC下会自动释放)
    栈 由编译器自动分配释放,存放函数的参数值、局部变量的值等

    6. UIButton继承自什么?为什么?

    UIButton是一个可以响应事件的控件,因此它的直接父类是UIControlUIControl的直接父类是UIView
    UIButton从父类UIControl那继承了控制相关的方法,比如添加事件、移除事件等

    7. 响应链与事件传递

    https://raw.githubusercontent.com/WuOtto/imgSrc/master/iOS_DefaultResponderChain.png

    UIResponder响应者对象,只要继承自UIResponder的类,才能处理事件。

    UIApplicationUIViewUIViewController都是继承自UIResponder类,可以响应和处理事件。CALayer不是UIResponder的子类,无法处理事件。

    事件的分发与传递:

    1. 当iOS程序中发生触摸事件后,系统会将事件加入到UIApplication管理的一个任务队列中
    2. UIApplication将处于任务队列最前端的事件向下分发。即UIWindow。
    3. UIWindow将事件向下分发,即UIView。
    4. UIView首先看自己是否能处理事件,触摸点是否在自己身上。如果能,那么继续寻找子视图。
    5. 遍历子控件,重复以上两步。
    6. 如果没有找到,那么自己就是事件处理者。如果
    7. 如果自己不能处理,那么不做任何处理。
      其中UIView不接受事件处理的情况主要有以下三种
    1. alpha <0.01
    2. userInteractionEnabled = NO
    3. hidden = YES.
    

    响应者链:

    响应链是从最合适的view开始传递,处理事件传递给下一个响应者,响应者链的传递方法是事件传递的反方法,如果所有响应者都不处理事件,则事件被丢弃。我们通常用响应者链来获取上几级响应者,方法是UIRespondernextResponder方法。

    8. 请写出下面这段代码的输出

    NSString *str1 = [NSString stringWithFormat:@"hello"];
    NSString *str2 = @"hello";
    NSString *str3 = @"hello";
        
    if (str1 == str2) {
        NSLog(@"str1 = str2");
    }
        
    if (str2 == str3) {
        NSLog(@"str2 = str3");
    }
        
    if ([str1 isEqualToString:str2]) {
        NSLog(@"str1 isEqualToString:str2");
    }
    

    输出如下:

    str2 = str3
    str1 isEqualToString:str2
    

    这里考察的是常量池相关的知识点
    isEqualToString比较的是两个字符串的内容;
    ==比较的是地址的引用;
    这里str2 == str3返回true,主要是与常量池有关;在给str2赋值的时候,将hello一起放入了常量池中,当再次将hello赋值给str3的时候,先从常量池中查看是否存在hello的值,如果有,则直接取出。所以str2和str3指的是同一个引用,因此返回的结果自然是true

    扩展:
    iOS程序中的内存分为:堆区、栈区、全局区(静态区)、常量区、方法区

  • 相关阅读:
    2020.06.09 手写数字识别-小数据集
    2020.6.1 深度学习-卷积
    2020.05.22 垃圾邮件分类2
    2020.05.08 分类与监督学习,朴素贝叶斯分类算法
    2020.04.27 主成分分析
    2020.04.27 特征选择
    2020.04.26 逻辑回归实践
    2020.04.24 逻辑归回
    2020.04.21 线性回归算法
    15 手写数字识别-小数据集
  • 原文地址:https://www.cnblogs.com/wuotto/p/10616630.html
Copyright © 2020-2023  润新知