iOS那些坑

iOS面试题汇总(一)

2017-11-02  本文已影响30人  zgsddzwj

1. 并排两个label,宽度由内容决定。父级View宽度不够时,优先显示左边label的内容

 // label1: 位于左上角
    [_label1 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(_contentView1.mas_top).with.offset(5);
        make.left.equalTo(_contentView1.mas_left).with.offset(2);

        // 40高度
        make.height.equalTo(@40);
    }];

    // label2: 位于右上角
    [_label2 mas_makeConstraints:^(MASConstraintMaker *make) {
        //左边贴着label1
        make.left.equalTo(_label1.mas_right).with.offset(2);

        //上边贴着父view
        make.top.equalTo(_contentView1.mas_top).with.offset(5);

        //右边的间隔保持大于等于2,注意是lessThanOrEqual
        //这里的“lessThanOrEqualTo”放在从左往右的X轴上考虑会更好理解。
        //即:label2的右边界的X坐标值“小于等于”containView的右边界的X坐标值。
        make.right.lessThanOrEqualTo(_contentView1.mas_right).with.offset(-2);

        //只设置高度40
        make.height.equalTo(@40);
    }];

 //设置label1的content hugging 为1000
    [_label1 setContentHuggingPriority:UILayoutPriorityRequired
                               forAxis:UILayoutConstraintAxisHorizontal];

    //设置label1的content compression 为1000
    [_label1 setContentCompressionResistancePriority:UILayoutPriorityRequired
                                             forAxis:UILayoutConstraintAxisHorizontal];

    //设置右边的label2的content hugging 为1000
    [_label2 setContentHuggingPriority:UILayoutPriorityRequired
                               forAxis:UILayoutConstraintAxisHorizontal];

    //设置右边的label2的content compression 为250
    [_label2 setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
                                             forAxis:UILayoutConstraintAxisHorizontal];

参考:http://tutuge.me/2015/05/23/autolayout-example-with-masonry/

2.KVO、KVC的原理及其使用方法

KVC在某种程度上提供了替代存取方法(访问器方法)的方案,不过存取方法终究是个好东西,以至于只要有可能,KVC也尽可能先尝试使用存取方法访问属性。当使用KVC访问属性时,它内部其实做了很多事:
1.首先查找有无<property>,set<property>,is<property>等property属性对应的存取方法,若有,则直接使用这些方法;
2.若无,则继续查找<property>,_get<property>,_set<property>等方法,若有就使用;
3.若查询不到以上任何存取方法,则尝试直接访问实例变量<property>,<property>;
4.若连该成员变量也访问不到,则会在下面方法中抛出异常。之所以提供这两个方法,就是让你在因访问不到该属性而程序即将崩掉前,供你重写,在内做些处理,防止程序直接崩掉。
valueForUndefinedKey:和setValue:forUndefinedKey:方法。

当某个类的对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中被观察属性的 setter 方法,在setter方法里使其具有通知机制。因此,要想KVO生效,必须直接或间接的通过setter方法访问属性(KVC的setValue就是间接方式)。直接访问成员变量KVO是不生效的。
同时派生类还重写了 class 方法以“欺骗”外部调用者它就是起初的那个类。然后系统将这个对象的 isa 指针指向这个新诞生的派生类,因此这个对象就成为该派生类的对象了,因而在该对象上对 setter 的调用就会调用重写的 setter,从而激活键值通知机制。此外,派生类还重写了 dealloc 方法来释放资源。

参考链接:http://www.jianshu.com/p/66bda10168f1

3.UITableView和UICollectionView的区别

关于UICollectionView的实现,网上一大片,我就不详细描述了。

4. synthesize 和 dynamic

synthesize默认生成getter、setter和对应的成员变量

5.self.nameArray = [NSMutaleArray alloc] init]; 这句话会引起什么问题?

_nameArray = [NSMutaleArray alloc] init];

参考:iOS .(点语法)和_(下划线)的使用原则

6.assign 与weak的区别

  • assign其实也可以用来修饰对象。那么我们为什么不用它修饰对象呢?因为被assign修饰的对象(一般编译的时候会产生警告:Assigning retained object to unsafe property; object will be released after assignment)在释放之后,指针的地址还是存在的,也就是说指针并没有被置为nil,造成野指针。对象一般分配在堆上的某块内存,如果在后续的内存分配中,刚好分到了这块地址,程序就会崩溃掉。
  • 那为什么可以用assign修饰基本数据类型?因为基础数据类型一般分配在栈上,栈的内存会由系统自己自动处理,不会造成野指针。
    weak修饰的对象在释放之后,指针地址会被置为nil。所以现在一般弱引用就是用weak。weak使用场景:
  • 在ARC下,在有可能出现循环引用的时候,往往要通过让其中一端使用weak来解决,比如: delegate代理属性,通常就会声明为weak。
  • 自身已经对它进行一次强引用,没有必要再强引用一次时也会使用weak。比如:自定义 IBOutlet控件属性一般也使用weak,当然也可以使用strong。

7.为什么IBOutlet属性是weak的?

8.NSNotificationCenter实现原理?

9.如何理解iOS中事件的传递和响应机制

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ 
    // 1.自己先处理事件...
    NSLog(@"do somthing...");
    // 2.再调用系统的默认做法,再把事件交给上一个响应者处理
    [super touchesBegan:touches withEvent:event]; 
}

参考:史上最详细的iOS之事件的传递和响应机制-原理篇

10.关于GCD中的串行,并行,同步,异步

注意:串行 queue 每次只能执行一个任务,可以使用它来代替锁,保护共享资源或可变的数据结构,串行queue确保任务按可预测的顺序执行(这是比锁好的地方)

dispatch_sync(dispatch_get_main_queue(), ^{
    [self doSomething:@"A"];
});

这段代码会造成线程死锁,同步串行,因为是同步所以当任务添加进去后要等block里面的内容执行完了才能继续其他动作,而又因为是串行主线程,所以block里面的内容需要等添加任务的代码完成了之后才能进行,因此会出现相互等待造成线程死锁

关于GCD的使用,可以参考:iOS中GCD的使用小结

11.GCD和NSOperation的区别

  • GCD是底层的C语言构成的API,而NSOperationQueue及相关对象是Objc的对象。在GCD中,在队列中执行的是由block构成的任务,这是一个轻量级的数据结构;而Operation作为一个对象,为我们提供了更多的选择;
  • 在NSOperationQueue中,我们可以随时取消已经设定要准备执行的任务(当然,已经开始的任务就无法阻止了),而GCD没法停止已经加入queue的block(其实是有的,但需要许多复杂的代码);
  • NSOperation能够方便地设置依赖关系,我们可以让一个Operation依赖于另一个Operation,这样的话尽管两个Operation处于同一个并行队列中,但前者会直到后者执行完毕后再执行;
  • 我们能将KVO应用在NSOperation中,可以监听一个Operation是否完成或取消,这样子能比GCD更加有效地掌控我们执行的后台任务;
    在NSOperation中,我们能够设置NSOperation的priority优先级,能够使同一个并行队列中的任务区分先后地执行,而在GCD中,我们只能区分不同任务队列的优先级,如果要区分block任务的优先级,也需要大量的复杂代码;
  • 我们能够对NSOperation进行继承,在这之上添加成员变量与成员方法,提高整个代码的复用度,这比简单地将block任务排入执行队列更有自由度,能够在其之上添加更多自定制的功能。

参考文献:

12.关于UIWindowLevel你懂多少?

UIKIT_EXTERN const UIWindowLevel UIWindowLevelNormal;
UIKIT_EXTERN const UIWindowLevel UIWindowLevelAlert;
UIKIT_EXTERN const UIWindowLevel UIWindowLevelStatusBar __TVOS_PROHIBITED;
2012-03-27 22:46:08.752 UIViewSample[395:f803] Normal window level: 0.000000
2012-03-27 22:46:08.754 UIViewSample[395:f803] Alert window level: 2000.000000
2012-03-27 22:46:08.755 UIViewSample[395:f803] Status window level: 1000.000000

这样印证了他们级别的高低顺序从小到大为Normal < StatusBar < Alert。

注意:当Level层级相同的时候,只有第一个设置为KeyWindow的显示出来,后面同级的再设置KeyWindow也不会显示。
UIWindow在显示的时候是不管KeyWindow是谁,都是Level优先的,即Level最高的始终显示在最前面

未完待续...

上一篇 下一篇

猜你喜欢

热点阅读