iOS记录

iOS面试资料(一)

2022-03-10  本文已影响0人  JamieChen

iOS面试题

[toc]

设计基本原则

简述六大设计基本原则(也称 SOLID 五大原则)

单一职责原则 (SRP, Single Responsibility Principle)

开闭原则(OCP, Open-Close Principle)

里氏替换原则 (LSP,Liskov Substitution Principle)

接口隔离原则(ISP, Interface Segregation Principle)

依赖倒置原则(DIP, Dependence Inversion Principle)

迪米特法则(LOD, Law Of Demeter) / 最小知道原则 (LKP,Least Knowledge Principle)

设计基本原则特点总结

面向对象的三大特征

什么是 MVC 设计模式?

什么是 MVVM?主要目的是什么?有哪些优点?

哪些类不适合使用单例模式?即使他们在周期中只会出现一次。

内存管理

引用计数怎么存储?

struct SideTable {
    spinlock_t stock;
    RefcountMap refcnts; // 存放着对象引用计数的散列表
    weak_table_t weak_table;
}

ARC具体为引用计数做了哪些工作?

深拷贝与浅拷贝

对象的拷贝

- (id)copyWithZone:(NSZone *)zone {
    Person *person = [Person allocWithZone:zone];
    person.name = self.name;
    return person;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
    Person *person = [Person allocWithZone:zone];
    person.name = self.name;
    return person;
}

集合对象的拷贝

#import <Foundation/Foundation.h>

@interface Person : NSObject <NSCoding>

@property (nonatomic, copy) NSString *name;

@end


#import "Person.h"

@implementation Person

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    self.name = [aDecoder decodeObjectForKey:@"name"];
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:self.name forKey:@"name"];
}

@end

自动释放池

属性修饰词:Copy、Strong、Weak、Assign

block属性为什么需要用copy来修饰?

代理为什么使用weak修饰?

为什么NSMutableArray一般不用copy修饰?

- (void)setData:(NSMutableArray *)data {
    if (_data != data) {
        [_data release];
        _data = [data copy];
    }
}

拷贝完成后:可变数组->不可变数组,在外操作时(添加、删除等)会存在问题

什么是“僵尸对象”?

有哪些情况会出现内存泄漏

- (void)dealloc底层执行了什么?

- (void)dealloc {
    _objc_rootDealloc(self);
}


void _objc_rootDealloc(id obj) {
    ASSERT(obj);
    obj->rootDealloc();
}


inline void objc_object::rootDealloc() {
    if (isTaggedPointer()) return;  // fixme necessary?

    if (fastpath(isa.nonpointer  &&          // 无优化过isa指针
                 !isa.weakly_referenced  &&  // 无弱引用
                 !isa.has_assoc  &&          // 无关联对象
                 !isa.has_cxx_dtor  &&       // 无cxx析构函数
                 !isa.has_sidetable_rc)) {   // 不存在引用计数器是否过大无法存储在isa中(使用 sidetable 来存储引用计数)
        // 直接释放
        assert(!sidetable_present());
        free(this);
    } else {
        // 下一步
        object_dispose((id)this);
    }
}


// 如果不能快速释放,则调用 object_dispose()方法,做下一步的处理
static id _object_dispose(id anObject) {
    if (anObject==nil) return nil;

    objc_destructInstance(anObject);
    
    anObject->initIsa(_objc_getFreedObjectClass ());

    free(anObject);
    return nil;
}


void *objc_destructInstance(id obj) {
    if (obj) {
        // Read all of the flags at once for performance.
        bool cxx = obj->hasCxxDtor();               // 是否存在析构函数
        bool assoc = obj->hasAssociatedObjects();   // 是否有关联对象

        // This order is important.
        if (cxx) object_cxxDestruct(obj);           // 销毁成员变量
        if (assoc) _object_remove_assocations(obj); // 释放动态绑定的对象
        obj->clearDeallocating();
    }
    return obj;
}


/*
 * clearDeallocating一共做了两件事
 *
 * 将对象弱引用表清空,即将弱引用该对象的指针置为nil
 * 2、清空引用计数表
 * - 当一个对象的引用计数值过大(超过255)时,引用计数会存储在一个叫 SideTable 的属性中
 * - 此时isa的 has_sidetable_rc 值为1,这就是为什么弱引用不会导致循环引用的原因
 */
inline void  objc_object::clearDeallocating() {
    if (slowpath(!isa.nonpointer)) {
        // Slow path for raw pointer isa.
        sidetable_clearDeallocating();
    }
    else if (slowpath(isa.weakly_referenced  ||  isa.has_sidetable_rc)) {
        // Slow path for non-pointer isa with weak refs and/or side table data.
        clearDeallocating_slow();
    }
    assert(!sidetable_present());
}

多线程

GCD

GCD核心概念:「任务」、「队列」

发送10个网络请求,然后再接收到所有回应之后执行后续操作,如何实现?

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(), ^{
// 合并图片
});
dispatch_group_enter()和dispatch_group_leave()

如何打造线程安全的NSMutableArray?

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

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(), ^{
        // 合并图片
});

什么是线程安全?

如何设置常驻线程?🌟🌟

在异步线程发送通知,在主线程接收通知。会不会有什么问题?

GCD线程是如何调度的

如何实现多个任务执行完后,再统一处理?

线程和线程之间如何通信?

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;

示例:

- (void)viewDidLoad {
    [super viewDidLoad];
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    // 在子线程中调用download方法下载图片
    [self performSelectorInBackground:@selector(download) withObject:nil];
}

- (void)download {
    // 1.根据URL网络中下载图片
    NSURL *urlstr=[NSURL URLWithString:@"fdsf"];

    // 2、把图片转换为二进制的数据, 这一行操作会比较耗时
    NSData *data=[NSData dataWithContentsOfURL:urlstr];

    // 3、把数据转换成图片
    UIImage *image=[UIImage imageWithData:data];

    // 4、回到主线程中设置图片
    [self performSelectorOnMainThread:@selector(settingImage:) withObject:image waitUntilDone:NO];
}

//设置显示图片
- (void)settingImage:(UIImage *)image {
    self.iconView.image=image;
}

谈谈atomic的实现机制,为什么不能保证绝对线程安全?

  - (void)setCurrentImage:(UIImage *)currentImage {
    if (_currentImage != currentImage) {
        [_currentImage release];
        _currentImage = [currentImage retain];
    }
}

- (UIImage *)currentImage {
    return _currentImage;
}

- (void)setCurrentImage:(UIImage *)currentImage {
    @synchronized(self) {
        if (_currentImage != currentImage) {
            [_currentImage release];
            _currentImage = [currentImage retain];
        }
    }
}

- (UIImage *)currentImage {
    @synchronized(self) {
        return _currentImage;
    }
}

进程和线程的区别

Notification与线程相关

如何实现在不同线程中post和转发一个Notification?

重定向的实现思路:

  1. 自定义一个通知队列(用数组类型),让它去维护那些我们需要重定向的Notification
  2. 我们仍然是像平常一样去注册一个通知的观察者,当Notification来了时,先看看post这个Notification的线程是不是我们所期望的线程
  3. 如果不是,则将这个Notification存储到我们的队列中,并发送一个信号(signal)到期望的线程中,来告诉这个线程需要处理一个Notification
  4. 指定的线程在收到信号后,将Notification从队列中移除,并进行处理

Notification的使用场景是什么?同步还是异步?

dispatch_once底层实现

线程锁

互斥锁

自旋锁

信号量(Semaphore - dispatch_semaphore_t)

递归锁(NSRecursiveLock)

atomic

线程不安全

上一篇 下一篇

猜你喜欢

热点阅读