• IOS 设计 面试题及答案


    本文大部分内容源自:http://www.cnblogs.com/SharkBin/p/4618388.html

    有一部分是自己看到感觉不错之后总结的:

    本篇的面试题是我认为比较好的iOS开发基础知识点,希望大家看过这后在理解的基础上掌握而不是死记硬背。死记硬背很快也会忘记的。

    1 iOS基础

    1.1 父类实现深拷贝时,子类如何实现深度拷贝。父类没有实现深拷贝时,子类如何实现深度拷贝。

    • 深拷贝同浅拷贝的区别:浅拷贝是指针拷贝,对一个对象进行浅拷贝,相当于对指向对象的指针进行复制,产生一个新的指向这个对象的指针,那么就是有两个指针指向同一个对象,这个对象销毁后两个指针都应该置空。深拷贝是对一个对象进行拷贝,相当于对对象进行复制,产生一个新的对象,那么就有两个指针分别指向两个对象。当一个对象改变或者被销毁后拷贝出来的新的对象不受影响。

    • 实现深拷贝需要实现NSCoying协议,实现- (id)copyWithZone:(NSZone *)zone 方法。当对一个property属性含有copy修饰符的时候,在进行赋值操作的时候实际上就是调用这个方法。

    • 父类实现深拷贝之后,子类只要重写copyWithZone方法,在方法内部调用父类的copyWithZone方法,之后实现自己的属性的处理

    • 父类没有实现深拷贝,子类除了需要对自己的属性进行处理,还要对父类的属性进行处理。

    1.2 KVO,NSNotification,delegate及block区别

    • KVO就是cocoa框架实现的观察者模式,一般同KVC搭配使用,通过KVO可以监测一个值的变化,比如View的高度变化。是一对多的关系,一个值的变化会通知所有的观察者。
    • NSNotification是通知,也是一对多的使用场景。在某些情况下,KVO和NSNotification是一样的,都是状态变化之后告知对方。NSNotification的特点,就是需要被观察者先主动发出通知,然后观察者注册监听后再来进行响应,比KVO多了发送通知的一步,但是其优点是监听不局限于属性的变化,还可以对多种多样的状态变化进行监听,监听范围广,使用也更灵活。

    • delegate 是代理,就是我不想做的事情交给别人做。比如狗需要吃饭,就通过delegate通知主人,主人就会给他做饭、盛饭、倒水,这些操作,这些狗都不需要关心,只需要调用delegate(代理人)就可以了,由其他类完成所需要的操作。所以delegate是一对一关系。

    • block是delegate的另一种形式,是函数式编程的一种形式。使用场景跟delegate一样,相比delegate更灵活,而且代理的实现更直观。

    • KVO一般的使用场景是数据,需求是数据变化,比如股票价格变化,我们一般使用KVO(观察者模式)。delegate一般的使用场景是行为,需求是需要别人帮我做一件事情,比如买卖股票,我们一般使用delegate。
      Notification一般是进行全局通知,比如利好消息一出,通知大家去买入。delegate是强关联,就是委托和代理双方互相知道,你委托别人买股票你就需要知道经纪人,经纪人也不要知道自己的顾客。Notification是弱关联,利好消息发出,你不需要知道是谁发的也可以做出相应的反应,同理发消息的人也不需要知道接收的人也可以正常发出消息。

    1.3 KVC如果实现,如何进行键值查找。KVO如何实现

    请看这两篇博文 KVC KVO

    1.4 将一个函数在主线程执行的4种方法

    • GCD方法,通过向主线程队列发送一个block块,使block里的方法可以在主线程中执行。
    dispatch_async(dispatch_get_main_queue(), ^{      
        //需要执行的方法
    });
    • NSOperation 方法
    NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];  //主队列
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
    	//需要执行的方法
    }];
    [mainQueue addOperation:operation];
    • NSThread 方法
    [self performSelector:@selector(method) onThread:[NSThread mainThread] withObject:nil waitUntilDone:YES modes:nil];
    
    [self performSelectorOnMainThread:@selector(method) withObject:nil waitUntilDone:YES];
    
    [[NSThread mainThread] performSelector:@selector(method) withObject:nil];
    • RunLoop方法
    [[NSRunLoop mainRunLoop] performSelector:@selector(method) withObject:nil];
    

    1.5 如何让计时器调用一个类方法

    • 计时器只能调用实例方法,但是可以在这个实例方法里面调用静态方法。
    • 使用计时器需要注意,计时器一定要加入RunLoop中,并且选好model才能运行。scheduledTimerWithTimeInterval方法创建一个计时器并加入到RunLoop中所以可以直接使用。
    • 如果计时器的repeats选择YES说明这个计时器会重复执行,一定要在合适的时机调用计时器的invalid。不能在dealloc中调用,因为一旦设置为repeats 为yes,计时器会强持有self,导致dealloc永远不会被调用,这个类就永远无法被释放。比如可以在viewDidDisappear中调用,这样当类需要被回收的时候就可以正常进入dealloc中了。
     [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
    
    -(void)timerMethod
    {
    //调用类方法
    	[[self class] staticMethod];
    }
    
    -(void)invalid
    {
    	[timer invalid];
    	timer = nil;
    }

    1.6 如何重写类方法

    • 1、在子类中实现一个同基类名字一样的静态方法
    • 2、在调用的时候不要使用类名调用,而是使用[self class]的方式调用。原理,用类名调用是早绑定,在编译期绑定,用[self class]是晚绑定,在运行时决定调用哪个方法。

    1.7 NSTimer创建后,会在哪个线程运行。

    • 用scheduledTimerWithTimeInterval创建的,在哪个线程创建就会被加入哪个线程的RunLoop中就运行在哪个线程
    • 自己创建的Timer,加入到哪个线程的RunLoop中就运行在哪个线程。

    1.8 id和NSObject*的区别

    • id是一个 objc_object 结构体指针,定义是
    typedef struct objc_object *id
    • id可以理解为指向对象的指针。所有oc的对象 id都可以指向,编译器不会做类型检查,id调用任何存在的方法都不会在编译阶段报错,当然如果这个id指向的对象没有这个方法,该崩溃还是会崩溃的。

    • NSObject *指向的必须是NSObject的子类,调用的也只能是NSObjec里面的方法否则就要做强制类型转换。

    • 不是所有的OC对象都是NSObject的子类,还有一些继承自NSProxy。NSObject *可指向的类型是id的子集。

    我的理解如果有错漏请一定指出,非常感谢!

    以下内容后续补充

    iOS 核心框架

    • CoreAnimation
    • CoreGraphics
    • CoreLocation
    • AVFoundation
    • Foundation

    iOS核心机制

    • UITableView 重用
    • ObjC内存管理;自动释放池,ARC如何实现
    • runloop
    • runtime
    • Block的定义、特性、内存区域、如何实现
    • Responder Chain
    • NSOperation
    • GCD

    数据结构

    • 8大排序算法
    • 二叉树实现
    • 二分查找实现

    面向对象编程

    • 封装、继承、多态

    • 设计模式6个原则

    • 设计一个类的功能,如何划分粒度(单一职责)

    • 接口隔离。

    • 如果有一个鸟类,有飞的动作,一个鸵鸟继承它是合适的吗(里氏替换)

    • 类之间的依赖如何依赖偶合度最小(依赖倒转)
      高层依赖低层,低层不能依赖高层。依赖接口,不能依赖具体的类。

    • 如果A要调用C函数,但C是B的成员类,应该如何设计?(迪米特)

    • 如何设计类,能做到只增加代码,而不修改代码,有哪些经验(开放封闭)
      通过设计模式解决。

    计算机技术

    • 计算机网络: TCP/IP、HTTPCDN、SPDY
    • 计算机安全: RSA、AES、DES
    • 操作系统:线程、进程、堆栈、死锁、调度算法

    iOS新特性、新技术

    • iOS7 UIDynamic、SpritKit、新布局、扁平化
    • iOS8 应用程序扩展、HealthKit、SceneKit、CoreLocation、TouchID、PhotoKit
    • iOS9
    • Apple Watch
    • 第三方库:SDWebImage、AFNetwork、JSONKit、wax
    • swift
     
     

    这些面试题都是 Objective-C基础面试题,一起来看看。

    1、#import和#include的区别,@class代表什么?

    @class一般用于头文件中需要声明该类的某个实例变量的时候用到,在m文件中还是需要使用#import

    而#import比起#include的好处就是不会引起重复包含

    2、谈谈Object-C的内存管理方式及过程?

    1.当你使用new,alloc和copy方法创建一个对象时,该对象的保留计数器值为1.当你不再使用该对象时,你要负责向该对象发送一条release或autorelease消息.这样,该对象将在使用寿命结束时被销毁.

    2.当你通过任何其他方法获得一个对象时,则假设该对象的保留计数器值为1,而且已经被设置为自动释放,你不需要执行任何操作来确保该对象被清理.如果你打算在一段时间内拥有该对象,则需要保留它并确保在操作完成时释放它.

    3.如果你保留了某个对象,你需要(最终)释放或自动释放该对象.必须保持retain方法和release方法的使用次数相等.

    3、Object-C有私有方法吗?私有变量呢?

    objective-c – 类里面的方法只有两种, 静态方法和实例方法. 这似乎就不是完整的面向对象了,按照OO的原则就是一个对象只暴露有用的东西. 如果没有了私有方法的话, 对于一些小范围的代码重用就不那么顺手了. 在类里面声名一个私有方法

    @interface Controller : NSObject { NSString *something; }

    + (void)thisIsAStaticMethod;

    – (void)thisIsAnInstanceMethod;

    @end

    @interface Controller (private) -

    (void)thisIsAPrivateMethod;

    @end

    @private可以用来修饰私有变量

    在Objective‐C中,所有实例变量默认都是私有的,所有实例方法默认都是公有的

    4、Object-C有多继承吗?没有的话用什么代替?cocoa 中所有的类都是NSObject 的子类

    多继承在这里是用protocol 委托代理 来实现的

    你不用去考虑繁琐的多继承 ,虚基类的概念.

    ood的多态特性 在 obj-c 中通过委托来实现.

    5、内存管理 Autorelease、retain、copy、assign的set方法和含义?

    1,你初始化(alloc/init)的对象,你需要释放(release)它。例如:

    NSMutableArray aArray = [[NSArray alloc] init]; 后,需要 [aArray release];

    2,你retain或copy的,你需要释放它。例如:

    [aArray retain] 后,需要 [aArray release];

    3,被传递(assign)的对象,你需要斟酌的retain和release。例如:

    obj2 = [[obj1 someMethod] autorelease];

    对象2接收对象1的一个自动释放的值,或传递一个基本数据类型(NSInteger,NSString)时:你或希望将对象2进行retain,以防止它在被使用之前就被自动释放掉。但是在retain后,一定要在适当的时候进行释放。

    关于索引计数(Reference Counting)的问题

    retain值 = 索引计数(Reference Counting)

    NSArray对象会retain(retain值加一)任何数组中的对象。当NSArray被卸载(dealloc)的时候,所有数组中的对象会 被 执行一次释放(retain值减一)。不仅仅是NSArray,任何收集类(Collection Classes)都执行类似操作。例如 NSDictionary,甚至UINavigationController。

    Alloc/init建立的对象,索引计数为1。无需将其再次retain。

    [NSArray array]和[NSDate date]等“方法”建立一个索引计数为1的对象,但是也是一个自动释放对象。所以是本地临时对象,那么无所谓了。如果是打算在全Class中使用的变量(iVar),则必须retain它。

    缺省的类方法返回值都被执行了“自动释放”方法。(*如上中的NSArray)

    在类中的卸载方法“dealloc”中,release所有未被平衡的NS对象。(*所有未被autorelease,而retain值为1的)

    6、浅拷贝和深拷贝区别是什么

    简单的来说就是,在有指针的情况下,浅拷贝只是增加了一个指针指向已经存在的内存,而深拷贝就是增加一个指针并且申请一个新的内存,使这个增加的指针指向这个新的内存,采用深拷贝的情况下,释放内存的时候就不会出现在浅拷贝时重复释放同一内存的错误

    7、C和obj-c 如何混用

    1)obj-c的编译器处理后缀为m的文件时,可以识别obj-c和c的代码,处理mm文件可以识别obj-c,c,c++代码,但cpp文件必须只能用c/c++代码,而且cpp文件include的头文件中,也不能出现obj-c的代码,因为cpp只是cpp

    2)在mm文件中混用cpp直接使用即可,所以obj-c混cpp不是问题

    3)在cpp中混用obj-c其实就是使用obj-c编写的模块是我们想要的。

    如果模块以类实现,那么要按照cpp class的标准写类的定义,头文件中不能出现obj-c的东西,包括#import cocoa的。实现文件中,即类的实现代码中可以使用obj-c的东西,可以import,只是后缀是mm。

    如果模块以函数实现,那么头文件要按c的格式声明函数,实现文件中,c++函数内部可以用obj-c,但后缀还是mm或m。

    总结:只要cpp文件和cpp include的文件中不包含obj-c的东西就可以用了,cpp混用obj-c的关键是使用接口,而不能直接使用 实现代 码,实际上cpp混用的是obj-c编译后的o文件,这个东西其实是无差别的,所以可以用。obj-c的编译器支持cpp

    8、Objective-C中类别和类扩展的区别。

    答案:category和extensions的不同在于后者可以添加属性。另外后者添加的方法是必须要实现的。

    extensions可以认为是一个私有的Category。

    9、我们说的Objective-C是动态运行时语言是什么意思?

    答案:多态。 主要是将数据类型的确定由编译时,推迟到了运行时。

    这个问题其实浅涉及到两个概念,运行时和多态。

    简单来说,运行时机制使我们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法。

    多态:不同对象以自己的方式响应相同的消息的能力叫做多态。意思就是假设生物类(life)都用有一个相同的方法-eat;

    那人类属于生物,猪也属于生物,都继承了life后,实现各自的eat,但是调用是我们只需调用各自的eat方法。

    也就是不同的对象以自己的方式响应了相同的消息(响应了eat这个选择器)。

    因此也可以说,运行时机制是多态的基础?

    10、Objective-C堆和栈的区别?

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

    申请大小:

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

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

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

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

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

  • 相关阅读:
    kafka 0.8.x producer Example(scala)
    Google V8扩展利器发布:v8-native-binding-generator
    beyond compare秘钥被禁
    STL算法之find
    十条nmap常用的扫描命令
    cgdb UTF-8乱码
    OpenWrt笔记
    openwrt hotplug
    git常用操作
    c99标准的restrict关键字
  • 原文地址:https://www.cnblogs.com/guiyangxueyuan/p/5377856.html
Copyright © 2020-2023  润新知