iOS一些面试问题

本文详细解析了Objective-C中的内存管理机制,包括ARC(自动引用计数)的基本概念及其实现原理,assign与weak的区别,__block与__weak的用法。同时,文中还探讨了atomic的线程安全性、retain cycle的产生原因、initialize与load方法的作用,以及与runtime机制相关的内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 什么是ARC?
    OC语言使用引用计数来管理内存,在没有引入ARC之前,要使用一个对象,就要retain该对象,释放时时release,这些都需要手动的来管理,比较麻烦。引入ARC之后,编译器来帮你做这些retain和release的工作,其实质内容还是基于引用计数,ARC的引入解决了程序员自己手动管理内存的麻烦。

  • 解释assign vs weak, __block vs __weak?
    assign ,仅仅将新值赋值给你的对象。对于非对象属性,这个是唯一的一个选择;assign 和weak的唯一区别就是,当使用weak属性指向的对象销毁了,此时该属性会被指向nil,而assign则不会。
    __block vs __weak: __block会持有该对象,即使超出了该对象的作用域,该对象还是会存在的,直到block对象从堆上销毁;而weak仅仅是将该对象赋值给_weak对象,当该对象销毁时,weak对象将指向nil;
    下面是一个简单的测试例子:

#import <Foundation/Foundation.h>
typedef  void  (^blk_t)(id);
int main(int argc, const char *argv[])
{
//    __block int val = 0;
//   
//    printf("栈上变量地址%p\n", &val);
//
//    void (^blk)(void) = [^{
//        val ++;
//        printf("堆上变量地址%p\n", &val);
//       } copy];
//    val = 10;
//        printf("val %d\n", val);
//   
//    blk();
//    printf("after val %d\n", val);
    blk_t blk;
    {
        id array = [NSMutableArray new];
        id __block array2 = array; // 会持有array对象,因此虽然超出了array的作用域,该对象还会存在直至block从堆上销毁
        id __weak  array3 = array; // 仅仅将array赋值给array3,因此当array销毁时,array3会指向nil

        NSLog(@"array2 地址: %p", array2);
        NSLog(@"array3 地址: %p", array3);
        blk = [^(id obj){
           // NSLog(@"block 中array2 地址:%p", array2);
            NSLog(@"block 中array3 地址:%p", array3);
//            [array2 addObject:obj];
//            NSLog(@"array2 count %lu", (unsigned long)[array2 count]);
        } copy];
    }
    blk([NSObject new]);
    blk([NSObject new]);
    return 0;
}
  • __block在arc和非arc下含义一样吗?
    不一样,在MRC下,__block不会增加对象的引用计数,因此可能会出现空悬指针问题,而在ARC里面_block则是对对象retain了,此时不会出现空悬指针问题。

  • 使用atomic一定是线程安全的吗?
    不是,atomic的本意是指属性的存取方法是线程安全的(thread safe),并不保证整个对象是线程安全的。比如,声明一个NSMutableArray的原子属性stuff,此时self.stuff 和self.stuff = othersulf都是线程安全的。但是,使用[self.stuff objectAtIndex:index]就不是线程安全的,需要用锁来保证线程安全性。

  • 描述一个你遇到过的retain cycle例子
    这个对象包含block值时容易出现循环引用的问题。

  • +(void)load; +(void)initialize;有什么用处?
    这个好有难度啊,先看官网的解释:
    +(void)initialize
    The runtime sends initialize to each class in a program just before the class, or any class that inherits from it, is sent its first message from within the program. The runtime sends the initialize message to classes in a thread-safe manner. Superclasses receive this message before their subclasses. The superclass implementation may be called multiple times if subclasses do not implement initialize—the runtime will call the inherited implementation—or if subclasses explicitly call [super initialize]. If you want to protect yourself from being run multiple times, you can structure your implementation along these lines:

+ (void)initialize {
  if (self == [ClassName self]) {
    // ... do the initialization ...
  }
}

Because initialize is called in a thread-safe manner and the order of initialize being called on different classes is not guaranteed, it’s important to do the minimum amount of work necessary in initialize methods. Specifically, any code that takes locks that might be required by other classes in their initialize methods is liable to lead to deadlocks. Therefore you should not rely on initialize for complex initialization, and should instead limit it to straightforward, class local initialization.
这段话的意思是initialize()方法的调用是在一个类或者其子类的在发送第一个消息之前,类似:

id objc_msgSend(id self, SEL _cmd, ...)
    {
        if(!self->class->initialized)
            [self->class initialize];
        ...send the message...
    }

这个类方法的lazily load的一个好处就是可以在这个方法里才做一些类的初始化的工作。有个问题就是如果一个父类实现了该方法而子类没有实现,因为类方法遵循方法的分发规则,因此父类的方法会被两次。因此一个书写方法是:

+ (void)initialize
    {
        if(self == [WhateverClass class])
        {
            ...perform initialization...
        }
    }

+(void)load
这个方法调用较早,当类加载的时候该方法就会被调用。这个方法一个特色就是如果一个类的分类也实现了该方法,那么类和分类都会调用该方法,这和一般的其他方法是有区别的,因为别的方法分类会将方法覆盖掉。还有一点就是子类如果没有实现该方法,那么该方法就不会被调用。
这两者的相同点就是:都仅仅调用一次(相对runtime而言,其实这两个方法可以手动的调用)。
还有一点需要注意的是调用顺序:
父类(Superclass)的方法优先于子类(Subclass)的方法,类中的方法优先于类别(Category)中的方法。

参考资料:
load和initialize

  • 为什么其他语言里叫函数调用, objective c里则是给对象发消息(或者谈下对runtime的理解)?
    这个主要牵扯到OC的runtime机制,消息查找>动态决议>消息转发。
  • 什么是method swizzling?
    是一种动态修改方法的实现,从而达到修改类行为的目的,其他知道的还有分类(Category)也能达到这个目的。
    可以参考这里:
    深入浅出Cocoa之 Method Swizzling

  • UIView和CALayer是啥关系?
    参考这里
    What are the differences between a UIView and a CALayer?

  • 如何高性能的给UIImageView加个圆角?(不准说layer.cornerRadius!)
    参考这里
    原理就是利用mask这个calyer属性,设置一个sharpLayer;

  • 使用drawRect有什么影响?
    使用不当,影响性能,尽量不使用。
    UIView的layoutSubviews和drawRect方法何时调用?

  • ASIHttpRequest或者SDWebImage里面给UIImageView加载图片的逻辑是什么样的?(把UIImageView放到UITableViewCell里面问更赞)?
    需要查看相关的文档来说明了。

  • 麻烦你设计个简单的图片内存缓存器(移除策略是一定要说的)?
    这个估计LRU(最近最常使用算法)的思路就可以了。

  • 讲讲你用Instrument优化动画性能的经历吧(别问我什么是Instrument)?
    这个真没有啊,悲哀。

  • loadView是干嘛用的?
    用于生成一个view,当self.view调用,如果此时view为nil,就会调用loadView来生成一个有效的view,如果我们没做什么,[super loadView]也可以生成 一个。一般很少用到吧。

  • viewWillLayoutSubView你总是知道的
    参考上面的一条专门讲解啥时候调用的。

  • GCD里面有哪几种Queue?你自己建立过串行queue吗?背后的线程模型是什么样的?
    这个比较熟悉了,两种,一种是串行的,一种是并行的。在访问数据库的时候建立的串行queue,防止发生数据访问问题。

  • 用过coredata或者sqlite吗?读写是分线程的吗?遇到过死锁没?咋解决的?
    这个遇到过,赶紧补补FMDB这一部分。

  • http的post和get啥区别?

参数的传递方式不同,GET,在URL之后,而POST是利用表单在HTTP包体中传递。
参数的长度限制不同:GET浏览器对URL整体长度有限制,而POST的长度则取决于服务器的处理能力,比GET方法要长
安全性上,GET没有POST高,因为参数信息在URL中,可能会泄露个人信息
总结一下,Get是向服务器发索取数据的一种请求,而Post是向服务器提交数据的一种请求,在FORM(表单)中,Method默认为”GET”,实质上,GET和POST只是发送机制不同,并不是一个取一个发!

  • 我知道你大学毕业过后就没接触过算法数据结构了,但是请你一定告诉我什么是Binary search tree? search的时间复杂度是多少?我很想知道!
    Binary search tree:二叉搜索树。
    主要由四个方法:(用C语言实现或者Python)

1.search:时间复杂度为O(h),h为树的高度
2.traversal:时间复杂度为O(n),n为树的总结点数。
3.insert:时间复杂度为O(h),h为树的高度。
4.delete:最坏情况下,时间复杂度为O(h)+指针的移动开销。

可以看到,二叉搜索树的dictionary operation的时间复杂度与树的高度h相关。所以需要尽可能的降低树的高度,由此引出平衡二叉树Balanced binary tree。它要求左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。这样就可以将搜索树的高度尽量减小。常用算法有红黑树、AVL、Treap、伸展树等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值