面试题集锦(1)

(一)   OBJC  面向对象的三大特征

封装:把具体的对象封装成抽象的类,隐藏内部实现,对象的属性和实现细节,仅对外公开接口,提高代码的安全性,实现模块化调用。

     ->好处:1.使用者只关注接口而不必关注实现2.代码的维护性好3.解藕

继承:描述类与类的关系,一个类是另外一个类的子类,子类可以复用父类的方法和属性

子类继承父类就会拥有父类所有的属性和方法,也可以重写父类的方法

     ->缺点:1.使程序变得复杂2.破坏了封装性3.降低了可维护性和可扩展性

多态:引用的多种形态,父类指针指向子类对象,子类可以重写父类的方法。不同对象以自己的方式响应相同的消息的能力叫做多态。

(二)    Category/Extension/Protocol

Category是分类,就是用来增强一个类的能力。Category就是可以在不用知晓一个类的具体内容,不改变原来代码的情况下,给它添加一些方法。可以覆盖但不能修改或删除方法(分类的优先级高).

1>分类的作用

   1、将类的实现分散到多个不同文件或多个不同框架中。

    2、创建对私有方法的前向引用。

    3、向对象添加非正式协议。

2>分类的局限性

      1、无法向类中添加新的实例变量,类别没有位置容纳实例变量(无法添加实例变量的局限可以使用运行时解决)。

       2、名称冲突,类别方法将完全取代初始方法从而无法再使用初始方法。

Extension是扩展,扩展是分类的一种特殊的形式,是没有名字的私有的分类,扩展就是用来声明私有的属性和方法.

Protocol是协议,只有.h文件,甚至可以不独立文件使用时 遵守协议,在本类中实现方法就可以,协议有required和optional的,required是必须实现的协议方法optional是可选实现的协议方法。

(三)  分类 拓展 协议中哪些可以声明属性(怎么使用@property)

都可以声明属性

   ->1.在protocol中使用property只会生成setter和getter方法声明,需要在遵守协议的对象中实现该属性方法

    ->2.category使用@property也是只会生成setter和getter方法的声明,如果我们真的需要给category增加属性的实现,需要借助于运行时的两个函数:1.objc_setAssociatedObject       2.objc_getAssociatedObject

      ->3.拓展中使用property,会自动生成setter和getter的方法和成员变量

      ->4.扩展一个类用Category好。继承会破坏程序的封装性。

      ->5.@property的本质就是在编译阶段由编译器自动帮我们生成ivar成员变量,getter方法,setter方法.

       ->6.@property的本质:@property = ivar + getter + setter;

       “属性”(property)有两大概念:ivar(实例变量)、存取方法(access method=getter + setter),主要的作用就在于封装对象中的数据

var、getter、setter是如何生成并添加到这个类中的?

完成属性定义后,编译器在编译期会自动编写访问这些属性所需的方法,此过程叫做“自动合成”(autosynthesis)

(四) 继承和类别的区别

1>使用继承:

        1.1>添加新方法和父类方法一致,但父类方法仍需要使用

         1.2>添加新属性

2>类别:

          2.1>针对系统提供的一些类,系统本身不提倡继承,因为这些类的内部实现对继承有所限制(NSString initWithFormat继承崩溃)

          2.2>类别可以将自己构建的类中的方法进行分组,对于大型的类,提高可维护性。

(五)  @property的关键字

 1>readwrite,readonly,assign,retain,copy,nonatomic属性的作用

     1.1>getter=getterName,setter=setterName,设置setter与getter的方法名

      1.2>assign,setter方法直接赋值,不进行任何retain操作,为解决原类型与循环引用问题(weak)-ARC用于简单数据类型

      1.3>retain,setter方法对参数进行release旧值再retain新值(strong)  

       1.4>copy,setter方法进行copy操作,与retain处理流程一样,先旧值release,再copy出新的对象,retainCount为1。这是为了减少对对象的依赖而引入的机制。

        1.5>readwrite,readonly,设置可供访问级别,readonly是只读特性只会生成getter方法 不会生成setter方法;不希望属性在类外改变

       1.6>nonatomic非原子性访问,不加同步,多线程并发访问会提高性能。默认的是为atomic原子型事务访问,同步,低性能。使用了atomic属性会严重影响性能 ;该属性使用了同步锁,会在创建时生成一些额外的代码用于帮助编写多线程程序,这会带来性能问题,通过声明nonatomic可以节省这些虽然很小但是不必要额外开销

(六)  使用atomic一定是线程安全的吗?

   1>不是,atomic的本意是指属性的存取方法是线程安全的,并不保证整个对象是线程安全的。 

   2>一个线程在连续多次读取某属性值的过程中有别的线程在同时改写该值,那么即便将属性声明为atomic,也还是会读到不同的属性值

(七)怎么用copy关键字?

 1> copy的使用场景

       copy的目的是为了让对象的属性不受外界影响,使用copy来创建对象的副本

 2>什么是深拷贝浅拷贝(深拷贝:对象拷贝,浅拷贝:指针拷贝)

     (1) 对于非容器类对象,不可变对象进行copy操作为浅拷贝,引用计数器加1,其他三种为深拷贝.       (2)对于容器类对象,基本和非容器类对象一致,但注意其深拷贝是对象本身是对象复制,其中元素仍为指针复制,系统将initWithArray方法归为了元素深拷贝,但其实如果元素为不可变元素,仍为指针复制,使用归解档可以实现真正的深拷贝,元素也是对象拷贝NSArray* trueDeepCopyArray = [NSKeyedUnarchiverunarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject: array]];

3>字符串为什么用copy 

           防止字符串内容改变

4>字符串所在内存区域

-->@“abc” 常量区      -->stringwithformat堆区        -->initWithString栈区

(八) 用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?

          ***   因为父类指针可以指向子类对象,使用copy的目的是为了让本对象的属性不受外界影响,使用copy无论给我传入是一个可变对象还是不可对象,本身持有的就是一个不可变的副本,保护其封装性.

          ***  如果我们使用是strong,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性.

          ***  这里需要注意的是集合对象的内容复制(对象复制)仅限于对象本身,对象元素仍然是指针复制

(九)  block

1> block属性为什么用copy? 

        MRC中,方法内部的block是在栈区的,使用copy可以把它放到堆区.在ARC中写不写都行:对于block使用copy还是strong效果是一样的,编译器自动对block进行了copy操作。

2> block使用注意什么?

循环引用(如果方法中的一些参数是 成员变量,那么可以造成循环引用)修改外部变量

3> block的主要使用场景?

(1)动画     (2)数组字典排序遍历   (3)回调状态     (4)错误控制

4> block是存储在堆中还是栈中

  默认情况下block存储在栈中,如果对block进行一个copy操作, block会转移到堆中

   如果block在栈中, block中访问了外界的对象,那么不会对对象进行retain操作

但是如果block在堆中, block中访问了外界的对象,那么会对外界的对象进行一次retain

5> block细节

block机制:block会把代码块里面的所有强指针对象强引用

如果在block中访问了外界的对象,一定要给对象加上__block/__weak,只要加上了__block/__weak,哪怕block在堆中,也不会对外界的对象进行retain(就不会强引用)

6> block和weak修饰符的区别?

     __block不管是ARC还是MRC模式下都可以使用,可以修饰对象,也可以修饰基本数据类型

__weak只能在ARC模式下使用,只能修饰对象(NSString),不能修饰基本数据类型

block修饰的对象可以在block中被重新赋值,weak修饰的对象不可以

常看到一个block要使用self,会处理成在外部声明一个weak变量指向self,在block里又声明一个strong变量指向weakSelf:weakSelf是为了block不持有self,避免循环引用,而再声明一个strongSelf是因为一旦进入block执行,就不允许self在这个执行过程中释放。block执行完后这个strongSelf会自动释放,不会有循环引用问题。

(十) 堆和栈的区别

1>管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak(内存泄漏)。

2>申请大小

栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。

堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

3>碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出

4>分配方式:堆都是动态分配的,没有静态分配。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。

5>分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的。

6>从数据存储方面来讲:

栈空间中一般存储基本类型,对象的地址

堆空间一般存放对象本身,block的copy等

(十一)  MRC&ARC内存管理机制

1<内存管理:对内存进行管理(分配内存,清理内存,作用于任何继承NSObject的对象(OC对象)

2<只有OC对象才需要进行内存管理的本质原因:1)OC对象存放于堆里面(需要手动释放)2)非OC对象一般放在栈里面(栈内存会被系统自动回收)

3<内存管理的原理:每个对象内部都保存了一个与之相关联的引用计数器,这个引用计数器是整型的。1.当使用alloc、new或者copy创建一个对象时,对象的引用计数器就会设置为12.给对象发送一条retain消息,对象引用计数器+1,给对象发送一条release消息,对象引用计数器-13.当一个对象的引用计数器值为0时,那么它将被销毁。

4<内存管理的原则有三点

 --> 1.一是谁创建,谁释放。不是你创建的,就不用你去释放

 --> 2.二是通常除了alloc、new或copy之外的方法创建的对象都被声明了autorelease

 --> 3.三是谁retain,谁release。只要调用了retain,无论这个对象是如何生成的,都要调用release

ARC:Automatic(自动) Reference(引用) Counting(计数)通过编译器在编译的时候,插入类似内存管理的代码来帮助开发者管理内存OC中的ARC的回收机制由编译器处理

MRC: Manul(手动) Refrence(引用) Counting(计数)    所有对象的内容都需要我们手动管理,需要程序员自己编写release/retain等代码(很容易导致内存泄露和坏内存问题)野指针:只要给一个僵尸对象(已经被销毁的对象)发送消息就会报错为了避免给野指针发送消息会报错,一般情况下,当一个对象被释放后我们会将这个对象的指针设置位空指针,因为在OC中给空指针发送消息是不会报错的

(十二) ARC下还会存在内存泄露吗?

  -->循环引用会导致内存泄露

  -->Objective-C对象与CoreFoundation(c语言)对象进行桥接的时候如果管理不当也会造成内存泄露

(十三)  autorelease原理

     自动释放池以栈的形式实现:当你创建一个新的自动释放池时,它将被添加到栈顶。当一个对象收到发送autorelease消息时,它被添加到当前线程的处于栈顶的自动释放池中,当自动释放池被回收时,它们从栈中被删除,并且会给池子里面所有的对象都会做一次release操作.

苹果是如何实现autoreleasepool的?

 autoreleasepool以一个队列数组的形式实现,主要通过下列三个函数完成.

(1) objc_autoreleasepoolPush   (2) objc_autoreleasepoolPop (3)objc_autorelease

(十四)  Runtime运行机制

     runtime,运行时机制,它是一套C语言库·实际上我们编写的所有OC代码,最终都是转成了runtime库的东西,比如类转成了runtime库里面的结构体等数据类型,方法转成了runtime库里面的C语言函数,平时调方法都是转成了objc_msgSend函数(所以说OC有个消息发送机制),因此,可以说runtime是OC的底层实现,是OC的幕后执行者.

        运行时是指在程序运行时才确定数据的类型,调用指定的方法。将数据类型的确定由编译时推迟到了程序运行时。

Runtime运行时,OC就是运行机制

C语言的函数调用,是在编译时决定调用哪个函数对于OC的函数,属于动态调用过程,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用(OC可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错)

1.消息机制:[obj foo];在objc动态编译时,会被转意为:objc_msgSend(obj, @selector(foo));当向一个对象发送消息时,objc_msgSend方法根据对象的isa指针找到对象的类,然后在类的调度表(dispatch table)中查找selector。如果无法找到selector,objc_msgSend通过指向父类的指针找到父类,并在父类的调度表(dispatch table)中查找selector,以此类推直到NSObject类。一旦查找到selector,objc_msgSend方法根据调度表的内存地址调用该实现。通过这种方式,message与方法的真正实现在执行阶段才绑定。如果找不到相应的方法时,程序在运行时会挂掉并抛出异常unrecognized selector sent to XXX(但是在这之前,objc的运行时会给出三次拯救程序崩溃的机会1.Method resolution 2.Fast forwarding 3.Normal forwarding

       为了保证消息发送与执行的效率,系统会将全部selector和使用过的方法的内存地址缓存起来。每个类都有一个独立的缓存,缓存包含有当前类自己的selector以及继承自父类的selector。查找调度表(dispatch table)前,消息发送系统首先检查receiver对象的缓存。缓存命中的情况下,消息发送(messaging)比直接调用方法(function call)只慢一点点点点。

2.方法的交换:利用Objective-C的动态特性,可以实现在运行时偷换selector对应的方法实现每个类都有一个方法列表,存放着方法名和方法实现的映射关系,selector的本质其实就是方法名,IMP有点类似函数指针,指向具体的Method实现,通过selector就可以找到对应的IMP(selector:方法名,IMP:方法的实现)

交换方法的几种实现方式

   (1)·利用method_exchangeImplementations交换两个方法的实现

   (2)·利用class_replaceMethod替换方法的实现

   (3)·利用method_setImplementation来直接设置某个方法的IMP

SEL    isa    super_class    _cmd IMP解释

          sel:一种类型,表示方法名称,类似字符串(可互转)

          isa:每个类都包含一个isa指针,指向对象的真实类型

         super_class:每一个类中还包含了一个super_class指针,用来指向父类对象

          _cmd:在Objective-C的方法中表示当前方法的selector,正如同self表示当前方法调用的对象实例

IMP定义为id (*IMP) (id, SEL,…) IMP是一个指向函数的指针,其实IMP就是方法的实现

3.动态添加方法;有没有使用performSelector,其实主要想问你有没有动态添加过方法。 如果一个类方法非常多,加载类到内存的时候也比较耗费资源,需要给每个方法生成映射表,可以使用动态给某个类,添加方法解决。

动态绑定:在运行时确定要调用的方法

      动态绑定将调用方法的确定也推迟到运行时。在编译时,方法的调用并不和代码绑定在一起,只有在消息发送出来之后,才确定被调用的代码。通过动态类型和动态绑定技术,您的代码每次执行都可以得到不同的结果。运行时因子负责确定消息的接收者和被调用的方法。运行时的消息分发机制为动态绑定提供支持。当您向一个动态类型确定了的对象发送消息时,运行环境系统会通过接收者 的isa指针定位对象的类,并以此为起点确定被调用的方法,方法和消息是动态绑定的。而且,您不必在Objective-C代码中做任何工作,就可以自动获取动态绑定的好处。您在每次发送消息时,特别是当消息的接收者是动态类型已经确定的对象时,动态绑定就会例行而透明地发生。

4.给分类添加属性给一个类声明属性,其实本质就是给这个类添加关联,并不是直接把这个值的内存空间添加到类存空间。runtime怎么添加属性、方法等

ivar表示成员变量

 (1)·class_addIvar       (2)class_addMethod           (3)class_addProperty

(4)·class_addProtocol    (5)class_replaceProperty

使用runtime Associate方法关联的对象,需要在主对象dealloc的时候释放么?

-->(1)·无论在MRC下还是ARC下均不需要

-->(2)·被关联的对象在生命周期内要比对象本身释放的晚很多,它们会在被NSObject-dealloc调用的object_dispose()方法中释放

runtime如何实现weak属性

     runtime对注册的类,会进行布局,会将weak对象放入一个hash表中。

      用weak指向的对象内存地址作为key,当对象销毁时,通过对象内存地址这个key,在weak hash表中搜索,找到所有weak对象,从而设置为nil。

 能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么

(1)不能向编译后得到的类中增加实例变量;(2)能向运行时创建的类中添加实例变量;

·分析如下:      因为编译后的类已经注册在runtime中,类结构体中的objc_ivar_list实例变量的链表和instance_size实例变量的内存大小已经确定,同时runtime会调用class_setIvarLayout或class_setWeakIvarLayout来处理strong weak引用,所以不能向存在的类中添加实例变量

     运行时创建的类是可以添加实例变量,调用class_addIvar函数,但是得在调用objc_allocateClassPair之后,objc_registerClassPair之前,原因同上。

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

推荐阅读更多精彩内容