Objective-C语言特性相关面试汇总

1.0 分类(Category)

问题1:你用分类做了哪些事?

        声明私有方法

        分解体积庞大的类文件

        把Framework的私有方法公开

问题2:分类的特点

        运行时决议

        可以为系统类添加分类

问题3:分类中都可以添加哪些内容?

        实例方法

        类方法

        协议

        属性(只生成对应get和set方法并未添加实例变量)

问题4:分类加载调用栈

        Objective-C语言特性相关面试汇总_第1张图片

问题5:分类实现原理

        运行时决议

        分类添加的方法可以覆盖(效果是覆盖实际宿主类的同名方法仍然存在)原类方法 消息传递的过程中优先查找靠前的元素。

        同名分类方法谁能生效取决于编译顺序

        名字相同的分类会引起编译报错

问题6:继承(inherit)

一个类(子类)继承于另一个类(父类),那么子类不仅拥有父类所有的属性和方法,而且可以创建属于自己的属性和方法。

类别和继承的使用,以下两种情况只能使用继承,类别无法实现。第一种,新扩展的方法与原方法同名,但是还需要使用父类的实现。因为类别会覆盖原类的实现,无法访问到原来的方法。第二种,扩展类的属性,这个类别无法做到。以下两种情况最好使用类别,第一种,针对系统提供的一些类,例如:NSString,NSArray,NSNumber等类,系统本身不提倡使用继承去扩展方法,因为这些类内部实现对继承有所限制,所以最后使用类别来进行方法扩展。第二种,类别支持开发人员针对自己构建的类,把相关的方法分组到多个单独的文件中,对于大型而复杂的类,这有助于提高可维护性,并简化单个源文件的管理。


2.0 关联对象

问题1:能否给分类添加成员变量?

        可以添加,但是不能在分类的声明和定义的时候直接为分类添加成员变量,可以通过关联对象的技术添加成员变量,来达到分类可以添加成员变量的效果。

Objective-C语言特性相关面试汇总_第2张图片

问题2:关联对象的本质

        关联对象由AssociationsManager管理并在AssociationsHashMap存储。

        所有对象的关联内容都在同一个全局容器中        

Objective-C语言特性相关面试汇总_第3张图片        

问题3:怎样清除某一个关联对象被关联的值?

        setObjectValue 传nil 就可以清除

        Objective-C语言特性相关面试汇总_第4张图片


3.0 扩展(Extension)

问题1:一般用扩展做什么?

        声明私有属性

        声明私有方法

        声明私有成员变量

问题2:扩展的特点

        编译时决议

        只以声明的形式存在,多数情况下寄宿于宿主类的.m中。

        不能为系统类添加扩展。


4.0 代理(Delegate)

问题1:什么是代理?

        一种软件设计模式,代理模式

        iOS中以@protocal形式体现

        传递方式一对一

问题2:代理的工作流程

        Objective-C语言特性相关面试汇总_第5张图片

问题3:代理使用过程中可能会遇到的问题?

        一般声明为weak以规避循环引用。 

Objective-C语言特性相关面试汇总_第6张图片


5.0 通知(NSNotification)

问题1:通知的特点

        使用观察者模式来实现的用于跨层传递消息的机制。

        传递方式为一对多

问题2:通知是如何实现一对多?

Objective-C语言特性相关面试汇总_第7张图片

问题3:如何实现通知机制?或者怎样实现通知机制?

        在通知中心 NSNotificationCenter系统类中内部维护一个Map表或者是一个字典,他的key是NotificationName就是我们addobserver的时候要传递的一个监听名称作为字典的key,它的value就是我们添加的observer,对于同一个NotificationName可能添加多个observer,所以对应的observer应该是一个组列表,列表中的每一个成员应该包含通知接收的一个观察者还要包含关于这个观察者调用的方法, 比如说:我们收到这个通知后,观察者回调方法是哪个,那么在这个列表当中元素中也会体现关于通知回调方法的相关数据信息。

Objective-C语言特性相关面试汇总_第8张图片


6.0 KVO

问题1:什么是kvo?

        kvo是观察者设模式的一种实现

        系统采用了isa(isa-swizzling)混写技术来实现kvo

问题2:isa混写技术在kvo中是如何实现的?

        Objective-C语言特性相关面试汇总_第9张图片

问题3:kvo的实现原理

当我们注册了一个对象的观察者的时候实际上是调用了addobserver:forkeypath:后系统会为我们在运行时动态的创建一个NSKVONotifying_A类,又会把原来类的将A的isa指针指向新创建的NSKVONotifying_A,把isa指针的指向修改实际上就是isa混写技术。NSKVONotifying_A是原来A类的子类,是为了重写原来类的setter的方法,然后子类通过对setter方法的重写来达到可以通知所有观察者的目的。

问题3.1:通过kvc设置value是否能生效?为什么能生效?

        可以生效       因为 setValue:forKey:最终会调用setter方法

问题3.2:通过成员变量直接赋值value能否生效

        不生效        因为无法触发系统的kvo,系统为我们提供的kvo相当于在setter方法中插入了willChangeValueForKey和didChangeValueForKey,所以我们也可以手动触发kvo方式来触发kvo生效 

如下图:

Objective-C语言特性相关面试汇总_第10张图片

kvo总结

         使用setter方法改变值kvo才会生效。

        使用setValue:forKey:改变值kvo才会生效。

        成员变量直接修改需手动添加kvo才会生效。 


7.0 KVC

键值编码技术

        -(id)valueForKey:(NSString *)key

        -(void)setValue:(id)value forKey:(NSString *)key

问题1:valueForKey调用流程

(1)首先会按照顺序依次查找getKey:、key、isKey、_key:这四个方法,只要找到这四个方法当中的任何一个就直接调用该方法;

(2)如果没有找到,那么这个时候会查看accessInstanceVariablesDirectly方法的返回值,如果返回的是NO(也就是不允许直接访问成员变量),那么会调用valueforUndefineKey:方法,并抛出异常“NSUnknownKeyException”;

(3)如果accessInstanceVariablesDirectly方法返回的是YES,也就是说可以访问其成员变量,那么就会按照顺序依次查找 _key、_isKey、key、isKey这四个成员变量,如果找到了,就直接取值;如果依然没有找到成员变量,那么会调用valueforUndefineKey方法,并抛出异常“NSUnknownKeyException”。

Objective-C语言特性相关面试汇总_第11张图片

问题2:setValue:forKey:调用流程

(1)首先会按照顺序依次查找setKey:方法和_setKey:方法,只要找到这两个方法当中的任何一个就直接传递参数,调用方法;

(2)如果没有找到setKey:和_setKey:方法,那么这个时候会查看accessInstanceVariablesDirectly方法的返回值,如果返回的是NO(也就是不允许直接访问成员变量),那么会调用setValue:forUndefineKey:方法,并抛出异常“NSUnknownKeyException”;

(3)如果accessInstanceVariablesDirectly方法返回的是YES,也就是说可以访问其成员变量,那么就会按照顺序依次查找 _key、_isKey、key、isKey这四个成员变量,如果查找到了,就直接赋值;如果依然没有查到,那么会调用setValue:forUndefineKey:方法,并抛出异常“NSUnknownKeyException”。
 

Objective-C语言特性相关面试汇总_第12张图片


8.0 属性关键字 

        读写权限

        原子性

        引用计数

读写权限

        readonly

        readwrite

原子性

        atomic (线程安全)举例:对数组添加和移除是无法保证安全的,只能保障赋值和获取

        nonatomic

  • atmoic:原子属性,为系统默认的属性,会为修饰的成员变量的setter方法自动加锁(自旋锁),使得线程安全,但较为消耗资源,效率相对低些。是一种单写多度的多线程技术,可能出现脏数据。

    atomic是一种自旋锁。当我们一旦获取了自旋锁,线程会一直保持该锁直至显示释放该锁。而在单一线程的操作中,其实并不需要去获取这个锁来防止多线程操作对于成员变量的同时修改,所以选择nonatomic可以避免多余的性能损耗

  • nonatomic:非原子属性,开发中最常用的属性,不会为修饰的成员变量的setter方法加锁,虽然线程不安全,但效率高

引用计数

        retain/strong

        assign/unsafe_unretained

        weak

        copy

assign:修饰基本数据类型,如int,BOOL等,修饰对象类型事,不改变其引用计数,修饰的对象在被释放后指针仍然指向原对象的内存地址,所以会产生垂悬指针

weak:不改变被修饰对象的引用计数,所修饰对象在被释放之后会被自动置为nil。

copy

浅拷贝

        Objective-C语言特性相关面试汇总_第13张图片

深拷贝

        Objective-C语言特性相关面试汇总_第14张图片

问题1:深拷贝和浅拷贝区别?

        是否开辟了新的内存空间

        是否影响了引用计数

        

copy关键字

Objective-C语言特性相关面试汇总_第15张图片

        可变对象copy和mutableCopy都是深拷贝。

        不可变对象copy都是浅拷贝,mutableCopy是深拷贝。

        copy方法返回的都是不可变对象。

 问题2:@property(copy)NSMutableArray *array ?

                会导致不可预计的程序异常问题,对array增删等操作会造成crash等行为。

                如果复制过来的是NSMutableArray,copy之后是NSArray

               如果赋值过来的是NSArray,copy之后是NSArray


MRC下如何重写retain修饰变量的setter方法?

@property (nonatomic,retain)id obj;
- (void)setObj:(id)obj{
    if (_obj != obj) {
        [_obj release];
        _obj = [obj retain];
    }
}

load 与 initialize 的区别​​​​​​​

以main为分界,load方法在main函数之前执行,initialize在main函数之后执行

  1. 只要程序启动就会将所有类的代码加载到内存中, 放到代码区(无论该类有没有被使用到都会被调用)

  2. // load方法会在当前类被加载到内存的时候调用, 有且仅会调用一次

  3. // 如果存在继承关系, 会先调用父类的load方法, 再调用子类的load方法

 当当前类第一次被使用的时候就会调用(创建类对象的时候)
// initialize方法在整个程序的运行过程中只会被调用一次, 无论你使用多少次这个类都只会调用一次
// initialize用于对某一个类进行一次性的初始化
// initialize和load一样, 如果存在继承关系, 会先调用父类的initialize再调用子类的initialize
 

AppDelegate(应用程序生命周期)

#pragma mark 在应用程序加载完毕之后调用
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    NSLog(@"应用程序启动");
    return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
    NSLog(@"即将从前台进入后台");
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
    NSLog(@"已经从前台进入后台");
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
    NSLog(@"即将从后台进入前台");
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
    NSLog(@"已经从后台进入前台");
}
- (void)applicationWillTerminate:(UIApplication *)application
{
    NSLog(@"应用程序被杀死或关闭");
}

UIViewController(控制器生命周期)

- (instancetype)init{
    if (self = [super init]) {
        NSLog(@"1.init初始化");
    }
    return self;
}
//当时xib加载时
- (void)awakeFromNib{
    [super awakeFromNib];
    NSLog(@"2.Nib加载成功");
}
- (void)loadView{
    [super loadView];
    NSLog(@"3.加载view。");
}
- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"4.载入完成,可以进行自定义数据以及动态创建其他控件");
}
- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    NSLog(@"5.视图将出现在屏幕之前");
}
- (void)viewWillLayoutSubviews{
    [super viewWillLayoutSubviews];
    NSLog(@"6.将要对子视图进行调整");
}
- (void)viewDidLayoutSubviews{
    [super viewDidLayoutSubviews];
    NSLog(@"7.对子视图进行调整完毕");
}
- (void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];
    NSLog(@"8.视图已在屏幕上渲染完成");
}
- (void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    NSLog(@"9.视图将被从屏幕上移除");
}
- (void)viewDidDisappear:(BOOL)animated{
    [super viewDidDisappear:animated];
    NSLog(@"10.视图已经被从屏幕上移除");
}
- (void)dealloc{
    NSLog(@"11.视图被销毁,此处需要对你在init和viewDidLoad中创建的对象进行释放");
}
- (void)didReceiveMemoryWarning{
    [super didReceiveMemoryWarning];
    NSLog(@"12.内存警告");
}

UIView(view生命周期)

- (void)didAddSubview:(UIView *)subview{
    [super didAddSubview:subview];
    NSLog(@"1.当视图添加子视图时调用");
}
- (void)willRemoveSubview:(UIView *)subview{
    [super willRemoveSubview:subview];
    NSLog(@"2.当子视图从本视图移除时调用");
}
- (void)willMoveToSuperview:(nullable UIView *)newSuperview{
    [super willMoveToSuperview:newSuperview];
    NSLog(@"3.当视图即将加入父视图时 / 当视图即将从父视图移除时调用");
}
- (void)didMoveToSuperview{
    [super didMoveToSuperview];
    NSLog(@"4.当视图加入父视图时 / 当视图从父视图移除时调用");
}
- (void)willMoveToWindow:(nullable UIWindow *)newWindow{
    [super willMoveToWindow:newWindow];
    NSLog(@"5.当视图即将加入window视图时 / 当视图即将从window视图移除时调用");
}
- (void)didMoveToWindow{
    [super didMoveToWindow];
    NSLog(@"6.当视图加入window视图时 / 当视图从window视图移除时调用");
}

你可能感兴趣的:(iOS面试大全,iOS)