面试题

 一 、KVO与KVC

        KVC

         https://blog.csdn.net/yuwuchaio/article/details/80701681

         KVC 键值编码  通过字符串Key值去直接访问对象属性,通过KVC机制可以间接的访问对象的属性,KVC之所以能访问对象属性是因为遵从了NSKeyValueCoding协议,所有的NSObject类都遵从了这个协议,通过KVC访问对象的属性

         KVC内部查询key值的顺序

         当调用setVlaue:属性值 forKey :@"name"的代码的时候,底层的执行机制如下:

         程序优先调用set方法,代码通过setter方法完成设置。

         如果程序没有找到set方法,KVC机制会检查 +(BOOL)accessInstanceVariablesDirectly方法有没有返回YES,该方法默认返回YES,如果你重写了该方法返回NO,那么这一步KVC会执行setValue:forUndefinedKey:方法.因此,如果你不想让你的类被别人使用KVC的话,重写 +(BOOL)accessInstanceVariablesDirectly方法,让其返回NO即可,这样的话,如果KVC没有找到set属性名时,会直接调用setValue:forUndefinedKey:方法  一般开发者并不会重写+(BOOL)accessInstanceVariablesDirectly方法,接下来KVC会搜索该类里面有没有名为_的成员变量,无论是在类.h部分还是在类.m部分定义,也无论用了什么样的访问修饰符,只要存在_命名的变量,KVC都可以对该成员变量赋值.

         如果该类中既没有set方法,也没有_成员变量,KVC机制会搜索_is的成员变量

         如果该类还是没有_和_is成员变量,KVC机制再会继续搜索和is的成员变量(即没有下划线的成员变量),再给他们赋值.

         如果上面列出的方法或成员变量都不存在,系统将会执行该对象的setValue: forUndefineKey:方法,默认是抛出异常.

KVC如何处理异常

          KVC中最常见的异常时使用了错误的key,或者传递了nil,KVC中有专门的方法来处理这些异常 通常在KVC操作Model时,抛出异常的那两个方法是需要重写的,如果直接抛出异常导致app崩溃是不合理的 错误的<key> 重写setValue: forUndefinedKey: 方法 一般是直接打印出来即可,或自己做特殊处理 传了<nil> 重写setNilValueForKey:就可以了 

          -(void)setNilValueForKey:(NSString *)key{ NSLog(@"不能将%@设成nil",key);}  

KVC的使用

1.动态的取值和设值

2.用KVC来访问和修改私有变量

3.Model和字典转换

4.修改一些控件的内部属性

KVO

     手动触发KVO

  1.需要重写关闭属性的自动触发

     + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {

         BOOL automatic = NO;

         if ([theKey isEqualToString:@"name"]) {

         automatic = NO;

         }

         else {

         automatic = [super automaticallyNotifiesObserversForKey:theKey];

         }

         return automatic;

     }

 2.[self.redView addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];

    添加这两个方法

    [self.redView willChangeValueForKey:@"name"];

    [self.redView didChangeValueForKey:@"name"];

 此时就会自动调用

 底层实现原理:

    动态的创建了一个redView的类,重写了子类的set方法,在set方法中添加了willChangeValueForKey和didChangeValueForKey。

二、字典初始化传入nil崩溃

因为系统检测到传入的key的count和value的count不一致,进而导致崩溃,相当于越界了

三、UIImage imageNamed 与 imageWithContentsOfFile的差别

[UIImage imageNamed:]只适合与UI界面中小的贴图的读取,而一些比较大的资源文件应该尽量避免使用这个接口。

imageName的方式会在使用的时候系统会cache,程序员是无法处理cache的,这是由系统自动处理的,对于重复加载的图像,速度会提升很多,这样反而用户体验好。所以如果某张图片需要在应用中使用多次,或者重复引用,使用imageName的方式会更好

imageWithContentsOfFile的方式,在使用完成之后系统会释放,不会缓存下来,所以也就没有这样的问题。一般也不会把所有的图片都会缓存。有些图片在应用中只使用一两次的,就可以用这样的方式,比如新手引导界面的图片等等,就适合这样的方式。没有明显的界限。

四、iOS内存管理

栈(stack):栈是编译器自动分配并释放,用来存放函数的参数,局部变量。

 堆(heap):堆一般是程序员自己分配和释放,如果我们在使用的过程中,没有释放,那么等到程序完全结束,系统将会对堆中的内容进行回收。一般开发中的alloc就是存放在堆中的 

全局变量(静态变量)(static):全局变量和静态变量是单独存放的,因为他们的声明周期和整个程序的生命周期一致。释放的时间是由整个程序结束后系统负责回收。 

文字常量区:一般用来存放常量字符串,比如说String *str = @"hello world",他的释放也和全局变量一致,在程序结束后进行释放。

 程序代码区:用来存放函数的二进制内容的区域。

内存中存放的对象都是存放在堆上的,系统并不会释放堆上的内存,所以需要我们手动去管理。

内存管理分为ARC(自动内存管理)、MRC(手动内存管理)、自动释放池(autorelease pool)。

1.MRC时代,我们需要手动的添加retain和relase,

2ARC时代,不需要手动添加,系统会自动帮我们插入retain和relase,  

3自动释放池(autorelease pool)实际上只是把对release的调用延迟了,对于每一个Autorelease,系统只是把该Object放入了当前的Autorelease pool中,当该pool被释放时,该pool中的所有Object会被调用Release。实际上对于 [NSString stringWithFormat:1.0] 这类构造函数返回的对象都是autorelease的。

手动添加的autorelease pool会在大括号执行完之后释放池子中的对象。

每次初始化好的对象都是autorelease对象,ARC下不需要手动加autorelease,MRC下需要手动加autorelease,ARC系统会自动帮我们插入retain和relase,MRC下需要手动加。

每个runloop里面都会默认有autorelease pool,当runloop休眠或停止的时候,会释放autorelease pool,释放autorelease pool的时候会调用池子中的autorelease对象的relase方法,释放对象。主线程中的autorelease pool是由若干个autorelease page组成,系统自动把不同的对象放到不同的page中,当runloop休眠的时候释放对应的page。

在实际中的使用场景其实很明确了, 在程序中中有大量临时变量的时候最好手动创建.

最常出现大量变量的时候显然是循环/遍历, 我们常用的for循环, 以及enumerate其实跟autoreleasepool也有关, for循环是不自动创建autoreleasepool的, 而enumerate中已经自动创建了autoreleasepool, 值得注意的是高并发enumerate常常会出一些意外的问题, 例如对象被提前释放, 所以建议高并发情况下使用for循环(性能高于enumerate), 再手动添加autoreleasepool.

autorelease pool来避免频繁申请/释放内存(就是pool的作用了)。这个应该是相对比较好理解的。

main.m里面的自动释放池只有app退出时才会释放。

autorelase与ralase的区别:realse后的对象会马上释放,autorelase相当于是延迟对象的释放,调用该方法后,对象不会被马上释放,只有当前runloop运行结束的时候才会释放。

五 iOS深拷贝与浅拷贝

浅拷贝:浅拷贝并不拷贝对象本身,只是对指向对象的指针进行拷贝

深拷贝:直接拷贝对象到内存中一块区域,然后把新对象的指针指向这块内存

在iOS中并不是所有对象都支持Copy和MutableCopy,遵循NSCopying协议的类可以发送Copy协议,遵循NSMutableCopying协议的类可以发送MutableCopy消息。如果一个对象没有遵循这两个协议而发送Copy或者MutableCopy消息那么会发生异常。如果要遵循NSCopying协议,那么必须实现copyWithZone方法。如果要遵循NSMutableCopying协议那么必须实现mutableCopyWithZone方法。

系统非容器类(NSString)不可变对象调用Copy方法其实只是把当前对象的指针指向了原对象的地址,而调用mutableCopy方法则是新分配了一块内存区域并把新对象的指针指向了这块区域。对于可变对象来说调用Copy和MutableCopy方法都会重新分配一块内存。但是copy和mutableCopy的区别在于copy在复制对象的时候其实是返回了一个不可变对象,因此当调用方法改变对象的时候会崩溃

对于容器类只有不可变的容器执行copy操作才是浅拷贝,所以所有的容器类使用mutableCopy是深拷贝

六、首页嵌套的手势冲突

解决方式通过计算tableview的偏移量,来固定他的偏移量。

拖动图片放大的功能实现 可以在拖动列表的时候,在代理方法中设置图片的frame来实现

七 GCD 的使用

执行方式分为同步异步  队列分为串行队列和并行队列

同步执行方法  任务按照顺序执行、不会开启新的线程

异步执行任务   任务可以并行处理,会开启新的线程

串行队列   队列是放任务的,队列里面的任务按照顺序执行  主队列是串行队列,主队列对应着管理者主线程的那条队列

并行队列  多个任务同时执行   全局队列是并行队列

同步串行队列   只有当串行队列为主线程时会卡死当前线程

异步串行队列    如果是主队列,不会开启新线程  如果是自己创建的队列,会开启新线程,按照顺序执行任务

https://www.jianshu.com/p/b89915a331ca

同步并行队列      不会开启子线程 ,串行执行任务

异步并行队列   会开启新的线程,并发的执行任务

串行队列 同步执行与异步执行的区别 ? 同步队列 sync的任务需要立即执行.任务按照顺序执行。异步队列,只能保证async里面的任务是顺序执行,asyncblock外面的代码与asyncblock里面的代码不顺序执行

同步并行队列 与 异步串行队列的区别 ? 同步并行队列不会开启子线程,异步串行队列会开启子线程。

调度组控制网络请求   需要配合dispatch_group_enter(group);dispatch_group_leave(group)

八 、iOS 分类(category)、类扩展(extension)、协议(protocol)

分类 category

使用场景分析

1.扩展已有的类

有大量的子类,需要添加公用方法,但又无法修改它们的父类的情形(如系统类)。

一般是大量的功能代码已经形成,使用子类需要添加新类的头文件等。分类只能添加方法,不能添加属性。(下文会提到如何添加属性)

2.使用父类私有方法

已经存在了大量的子类方法,但是又无法修改他们的父类,比如系统自带的类添加类扩展方法。在子类中声明父类类别后,即可通过编译。

category、extension异同点分析

1、分类(category)

① category只能添加“方法”,不能添加成员变量。

② 分类中可以访问原来类中的成员变量,但是只能访问@protect和@public属性。

③ 添加方法加上前缀,添加方法会覆盖父类的同名方法,可以防止意外覆盖,也防止被别人覆盖。

④ 分类中添加的成员变量,要通过getter、setter方法进行添加。

类扩展(extension)

① 类扩展的属性和方法都是私有的,也可以定义在.h中,这样就是共有的,类扩展中的方法是一定要实现的方法。Category没有这个限制。

分类有.h和.m两个文件  扩展只有.h 方法的实现是放在原类中的

 九、消息传递

 1.void objc_msgSend(void /id self,SEL op/)

 当调用方法[self class] 相当于调用objc_msgSend(Self,@sel(class))

 void objc_msgSuperSend(void /struct objc_super *super,SEL op/),objc_super结构体中接受者receiver就是当前对象

 struct objc_super{

  id receiver;

 }

2.当调用方法的时候,会先在缓存中查找是否有该方法,没有的话通过isa指针去当前类方法列表中查找,没有的话再去父类查找,直到找到该方法,如果查找到根部(父类为nil比如NSObject)还没有,就会去调用消息转发机制

 消息转发后调用的方法:

 + (BOOL)resolveInstanceMethod:(SEL)sel

 - (id)forwardingTargetForSelector:(SEL)aSelector{

 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{

 - (void)forwardInvocation:(NSInvocation *)anInvocation

3.当一个类的方法没有实现的时候,可以通过runtime为方法添加实现

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容