iOS 头条一面 面试题

作者: iOS开发面试题技术合集 | 来源:发表于2020-08-08 15:35 被阅读0次

    原文地址

    1、如何高效的切圆角?

    切圆角共有以下三种方案:

    • cornerRadius + masksToBounds:适用于单个视图或视图不在列表上且量级较小的情况,会导致离屏渲染。

    • CAShapeLayer+UIBezierPath:会导致离屏渲染,性能消耗严重,不推荐使用。

    • Core Graphics:不会导致离屏渲染,推荐使用。

    2、什么是隐式动画和显式动画?

    隐式动画指的是改变属性值而产生的默认的过渡动画(如background、cornerRadius等),不需要初始化任何类,系统自己处理的动画属性;显式动画是指自己创建一个动画对象并附加到layer上,如 CAAnimation、CABasicAnimation、CAKeyframeAnimation

    3、UIView 和 CALayer 的区别?

    UIView 是 CALayer 的 delegate,UIView 可以响应事件,而 CALayer 则不能。

    4、离屏渲染?

    iOS 在不进行预合成的情况下不会直接在屏幕上绘制该图层,这意味着 CPU 和 GPU 必须先准备好屏幕外上下文,然后才能在屏幕上渲染,这会造成更多时间时间和更多的内存的消耗。

    5、Objective - C 是否支持方法重载(overloading)?

    不支持。方法重载(overloading):允许创建多项名称相同但输入输出类型或个数不同的方法。

    // 这两个方法名字是不一样的,虽然都是writeToFile开头
    -(void) writeToFile:(NSString *)path fromInt:(int)anInt;
    -(void) writeToFile:(NSString *)path fromString:(NSString *)aString;
    

    注:Swift 是支持的。

    func testFunc() {}
    func testFunc(num: Int) {}
    

    6、KVC 的应用场景及注意事项

    KVC(key-Value coding) 键值编码,指iOS开发中,可以允许开发者通过Key名直接访问对象的属性,或者给对象的属性赋值。不需要调用明确的存取方法,这样就可以在运行时动态访问和修改对象的属性,而不是在编译时确定。
    它的四个主要方法:

    - (nullable id)valueForKey:(NSString *)key;                          //直接通过Key来取值
    - (void)setValue:(nullable id)value forKey:(NSString *)key;          //通过Key来设值
    - (nullable id)valueForKeyPath:(NSString *)keyPath;                  //通过KeyPath来取值
    - (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;  //通过KeyPath来设值
    

    应用场景:

    • 动态取值和设值
    • 访问和改变私有变量
    • 修改控件的内部属性

    注意事项:

    • key 不要传 nil,会导致崩溃,可以通过重写setNilValueForKey:来避免。
    • 传入不存在的 key 也会导致崩溃,可以通过重写valueForUndefinedKey:来避免。

    7、如何异步下载多张小图最后合成一张大图?

    使用Dispatch Group追加block到Global Group Queue,这些block如果全部执行完毕,就会执行Main Dispatch Queue中的结束处理的block。

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, queue, ^{ /*加载图片1 */ });
    dispatch_group_async(group, queue, ^{ /*加载图片2 */ });
    dispatch_group_async(group, queue, ^{ /*加载图片3 */ }); 
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            // 合并图片
    });
    

    8、NSTimer 有什么注意事项?在 dealloc 中调用[timer invalidate];会避免循环引用吗?

    • 时间延后。如果 timer 处于耗时较长的 runloop 中,或者当前 runloop 处于不监视 timer 的 mode 时(如 scrollView 滑动时)。它在下次 runloop 才会触发,所以可能会导致比预期时间要晚。
    • 循环引用。target 强引用 timer,timer 强引用 target。

    时间延后

    使用 dispatch_source_t来提高时间精度。

    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    if (timer) {
        dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, interval * NSEC_PER_SEC), interval * NSEC_PER_SEC, (1ull * NSEC_PER_SEC) / 10);
        dispatch_source_set_event_handler(timer, block);
        dispatch_resume(timer);
    }
    

    循环引用
    在 dealloc 中调用 [timer invalidate];不会避免循环引用。因为 timer 会对 target 进行强引用,所以在 timer 没被释放之前,根本不会走 target 的 dealloc 方法。
    可以通过以下几种方法来避免:

    如果 iOS 10 及以上,可以使用nit(timeInterval:repeats:block:)。target 不再强引用 timer。记得在 dealloc 中调用 [timer invalidate];,否则会造成内存泄漏。

    timer = Timer(timeInterval: 1.0, repeats: true, block: { [weak self] (timer) in
        self?.timerFunc()
    })
    
    • 使用中间件的方式来避免循环引用。
    // 定义
    @implementation WeakTimerTarget
    {
        __weak target;
        SEL selector;
    }
    
    - (void)timerDidFire:(NSTimer *)timer {
        if(target) {
            [target performSelector:selector withObject:timer];
        } else{
            [timer invalidate];
        }
    }
    @end
    
    // 使用
    WeakTimerTarget *target = [[WeakTimerTarget alloc] initWithTarget:self selector:@selector(tick)];
    timer = [NSTimer scheduledTimerWithTimeInterval:30.0 target:target selector:@selector(timerDidFire:) ...];
    

    9、对 property 的理解

    @property = ivar + getter + setter;
    

    10、Notification 的注意事项

    在哪个线程发送通知,就在哪个线程接受通知。

    11、Runloop的理解

    一般来讲,一个线程一次只能执行一个任务,执行完成后线程就会退出。如果我们需要一个机制,让线程能随时处理事件但并不退出,通常的代码逻辑 是这样的:

    function loop() {
        initialize();
        do {
            var message = get_next_message();
            process_message(message);
        } while (message != quit);
    }
    

    12、对 OC 中 Class 的源码理解?其中 cache 的理解?

    Class 的底层用 struct 实现,源码如下:

    struct _class_t {
        struct _class_t *isa;
        struct _class_t *superclass;
        void *cache;
        void *vtable;
        struct _class_ro_t *ro;
    };
    

    Cache用于缓存最近使用的方法。一个类只有一部分方法是常用的,每次调用一个方法之后,这个方法就被缓存到cache中,下次调用时 runtime 会先在 cache 中查找,如果 cache 中没有,才会去 methodList 中查找。以此提升性能。

    13、项目优化做了哪些方面?

    • 删除无用资源文件及代码
    • 在合适的地方加缓存
    • 耗时长的代码异步执行

    14、如何一劳永逸的检测包的裂变(检测包的大小)?

    这个不知道,希望了解的朋友可以在评论区指出来。

    15、实现一个判断 IP 地址是否合法的方法

    func isIPAddress(str: String) -> Bool {
        guard !str.isEmpty else { return false }
        var isIPAddress = false
        let coms = str.components(separatedBy: ".")
        for com in coms {
            if let intCom = Int(com), intCom >= 0, intCom <= 255 {
                isIPAddress = true
            } else {
                isIPAddress = false
                return isIPAddress
            }
        }
        return isIPAddress
    }
    

    资料推荐

    如果你正在跳槽或者正准备跳槽不妨动动小手,添加一下咱们的交流群1012951431来获取一份详细的大厂面试资料为你的跳槽多添一份保障。

    相关文章

      网友评论

        本文标题:iOS 头条一面 面试题

        本文链接:https://www.haomeiwen.com/subject/bmjhdktx.html