面试题笔记1

来源:互联网 发布:新手开淘宝店怎么装修 编辑:程序博客网 时间:2024/06/06 00:39

1、简述OC中内存管理机制。与retain配对使用的方法是dealloc还是release,为什么?需要与alloc配对使用的方法是dealloc还是release,为什么?readwrite,readonly,assign,retain,copy,nonatomic 、atomic、strong、weak属性的作用?

答:与retain配对使用的是release,因为retain使引用计数加1,release使引用计数减一;与alloc配对使用的是dealloc,alloc是开辟内存空间,dealloc是释放内存;readwrite是读写,readonly是只读,assign是简单地赋值,对象的引用计数不变;retain使对象的引用计数加1;copy是创建了一个相同的对象,新对象的引用计数加一,旧对象的引用计数不变;atomic设置成员变量的@property属性时,默认为atomic,提供多线程安全。在多线程环境下,原子操作是必要的,否则有可能引起错误的结果。加了atomic,setter函数会变成下面这样:

{lock}      if (property != newValue) {            [property release];             property = [newValue retain];       }  {unlock}

  nonatomic 禁止多线程,变量保护,提高性能;在ARC中, 只要某一对象被任一strong指针指向,那么它就不会被销毁;如果对象没有被任何strong指针指向,那么就会被销毁。strong与retain类似,weak与assign类似。


2、类变量的@protected,@private,@public,@package,声明各有什么含义?

@protected的作用范围是类自身和其子类,什么都不写,默认是@protected,@private的作用范围是类自身,@public的作用范围在任何地方;使用modern运行时,一个@package实例变量在实现这个类的可执行文件镜像中实际上是@public的,但是在外面就是@private;个类型最常用于框架类的实例变量,使用@private太限制,使用@protected或者@public又太开放。

3.  线程是什么?进程是什么?二者有什么区别和联系?

进程(process)是一块包含了某些资源的内存区域,操作系统利用进程把他的工作划分为一些功能单元;进程中包含的一个或多个的执行单元成为线程(thread)。一个程序至少有一个进程,一个进程至少有一个线程。线程是进程的一个实体,是CPU调度和分派的基本单位

二者的联系:

  • 一个线程只能属于余一个进程,而一个进程可以有多个线程,但至少有一个线程;
  • 资源分配给进程,同一进程的所有线程共享该进程的所有资源;
  • 线程在执行过程中,需要些写作同步。不同进程的线程间要利用消息通信的方法实现同步;
  • 处理机分给线程,即真正在处理机上运行的是线程;
  • 线程是指进程内的一个执行单元,也是进城内的可调度实体。

二者的区别:

  • 调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位;
  • 并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可以并发执行;
  • 拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源;
  • 系统开销:在创建或撤销进程的时候,由于系统都要为之分配和回收资源,导致系统的明显大于创建或撤销线程时的开销。单进程有独立的地址空间,进程崩溃后,在保护模式下不会对其他的进程产生影响,而线程只是进程中的不同的执行路径。线程有自己的堆栈和局部变量,单线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费的资源较大,效率要差。

4、  谈谈你对多线程开发的理解?ios中有几种实现多线程的方法?

答:多线程的主要是用来执行一些耗时操作,例如网络图片、视频、歌曲、书籍等资源下载,游戏中的音乐播放等,充分发挥多核处理器的优势,并发(同时执行)任务让系统运行的更快、更流畅。

(1)NSThread

 创建方式主要有两种:

//此方法一调用就会立即创建一个线程来做事情

[NSThread detachNewThreadSelector:@selector(myThreadMainMethod:) toTarget:self withObject:nil];

//此方法要直到我们手动调用 start 启动线程时才会真正去创建线程。这种延迟实现思想在很多跟资源相关的地方都有用到。此方法我们还可以在启动线程之前,对线程进行配置,比如设置 stack 大小,线程优先级。

<span style="font-size:18px;">NSThread* myThread = [[NSThread alloc] initWithTarget:self selector:@selector(myThreadMainMethod:) object:nil];[myThread start]; //启动线程</span>

还可以使用NSObject的方法:

//开启后台线程执行任务的方法

<span style="font-size:18px;">- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg</span>

//在后台中通知主线程执行任务的方法:

–(void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;<span style="color: rgb(51, 51, 51); font-family: SimSun; font-size: 18px; line-height: 28px;">//涉及到UI界面更新的操作</span>

多线程的内存管理:

在OC中,每个线程都需要有@autoreleasepool,否则可能会出现内存泄露

2.NSOperation:

NSOperation是一个抽象的类,不能直接使用它,而是使用其子类(NSInvocationOperation或NSBlockOperation)进行实际操作,需要手动调用方法start来执行操作:

<span style="font-size:18px;">NSInvocationOperation * operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test ) object:nil];[operation start];</span>
<span style="font-size:18px;">NSBlockOperation * operation = [NSBlockOperation blockOperationWithBlock:^{    }];[operation addExecutionBlock:^{      }];</span><pre name="code" class="objc"><span style="font-size:18px;">[operation start];</span>
可以把NSOperationQueue看做是一个线程池,可与向线程池中添加NSOperation到队列中,添加到队列中的NSOperation不需要再手动调用start方法

3.GCD

<span style="font-size:18px;">dispatch_async(dispatch_queue_t queue,dispatch_block_t block);</span>
async表示异步执行,queue表示将要执行的方法交给谁进行处理,block代表要执行的方法
之所以用到多线程,是为了处理一些耗时的操作,避免阻塞主线程。GCD里有三种queue来处里

1.Main queue

在主线程运行,由dispatch_get_main_queue获得.和UI相关的就要使用MainQueue.

2.Serial quque(private dispatch queue)

每次运行一个任务,可以添加多个,执行次序FIFO. 通常是指程序员生成的.

3. Concurrent queue(globaldispatch queue):

可以同时运行多个任务,每个任务的启动时间是按照加入queue的顺序,结束的顺序依赖各自的任务.使用dispatch_get_global_queue获得.

三种多线程方式的区别:

  •        Thread 是这三种范式里面相对轻量级的,但也是使用起来最负责的,你需要自己管理thread的生命周期,线程之间的同步。线程共享同一应用程序的部分内存空间, 它们拥有对数据相同的访问权限。你得协调多个线程对同一数据的访问,一般做法是在访问之前加锁,这会导致一定的性能开销。在 iOS 中我们可以使用多种形式的 thread: Cocoa threads: 使用NSThread 或直接使用 NSObject 的类方法 performSelectorInBackground:withObject: 来创建一个线程。如果你选择thread来实现多线程,那么 NSThread 就是官方推荐优先选用的方式。
  •       Cocoa operations是基于 Obective-C实现的,类 NSOperation 以面向对象的方式封装了用户需要执行的操作,我们只要聚焦于我们需要做的事情,而不必太操心线程的管理,同步等事情,因为NSOperation已经为我 们封装了这些事情。 NSOperation 是一个抽象基类,我们必须使用它的子类。iOS 提供了两种默认实现:NSInvocationOperation 和 NSBlockOperation。
  •       Grand Central Dispatch (GCD): iOS4 才开始支持,它提供了一些新的特性,以及运行库来支持多核并行编程,它的关注点更高:如何在多个 cpu 上提升效率。

多线程之间的通信:

//在应用程序主线程中做事情:- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array//在指定线程中做事情:- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array//在当前线程中做事情://Invokes a method of the receiver on the current thread using the default mode after a delay.- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delayperformSelector:withObject:afterDelay:inModes://取消发送给当前线程的某个消息cancelPreviousPerformRequestsWithTarget:cancelPreviousPerformRequestsWithTarget:selector:object://如在我们在某个线程中下载数据,下载完成之后要通知主线程中更新界面等等,可以使用如下接口:- (void)myThreadMainMethod{    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];    // to do something in your thread job    ...    [self performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilDone:NO];    [pool release];}

5.  线程同步和异步的区别?IOS中如何实现多线程的同步?

线程同步是多个线程同时访问同一个资源,等待资源访问结束;线程异步是指访问资源时再空闲等待时同时访问其他资源

线程异步指两个线程之间各干各的,没什么关系,同步是指多线程同时操作一个数据,这个时候需要对数据添加保护,这个保护就是线程的同步
在IOS中有几种方法来解决多线程访问同一个内存地址的互斥问题:
  • @synchronized(id anObject),(最简单的方法):会自动对参数对象加锁,保证临界区内的代码线程安全
  • 使用NSLock
  • 原子操作是同步的一个简单地形式,他处理简单地数据类型。原子操作的优势是他们不妨碍竞争的线程。对于简单的操作,原子操作比使用锁具有更高的性能优势
7.获取一台设备唯一标识的方法有哪些?
  • UDID(Unique Device Identifier)已禁用
  • UUID(Universally Unique Identifier)苹果公司建议使用UUID为应用生成唯一标识字符串。开发者可以在应用第一次启动时调用一 次,然后将该串存储起来,以便以后替代UDID来使用。但是,如果用户删除该应用再次安装时,又会生成新的字符串,所以不能保证唯一识别该设备。如果使用UUID,就要考虑应用被删除后再重新安装时的处理。
  • MAC Address 但是现在如果用户升级到iOS7(及其以后的苹果系统)后,他们机子的MAC Address就是一样的,没办法做区分,只能弃用此方法,重新使用UUID来标识。
  • OPEN UDID 如果把使用了OpenUDID方案的应用全部都删除,再重新获取OpenUDID,此时的OpenUDID就跟以前的不一样。可见,这种方法还是不保险。
  • 广告标示符(IDFA-identifierForIdentifier)
  • Vindor标示符 (IDFV-identifierForVendor)
  • 推送token+bundle_id

8.   iOS类是否可以多继承?如果没有,那可以用其他方法实现吗?简述实现过程。

IOS不能多继承, 但可以通过其他方法变相的实现多继承。

e.g:1.使用多协议2.使用组合方式3.使用类目,可以为类添加方法, 是不能添加实例变量4.延展就是定义自己私有的方法

9.堆和栈的区别

预备知识:

一个由C/C++编译的程序占用的内存分为以下几个部分 
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其 
操作方式类似于数据结构中的栈。 
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回 
收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。 
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的 
全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另 
一块区域。 - 程序结束后由系统释放。 
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放 
5、程序代码区—存放函数体的二进制代码。 

主要的区别由以下几点:
    1、管理方式不同;
    2、空间大小不同;
    3、能否产生碎片不同;
    4、生长方向不同;
    5、分配方式不同;
    6、分配效率不同;
    管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。

10.  iOS本地数据存储都有哪几种方式?

1.sqlite3

2.归档

3.NSUserDfault

4.write写入方式

11.iOS动态类型和动态绑定、动态载入

1.动态类型识别常用方法

-(BOOL)isKindOfClass:classObj  是否是classObj类或其子类

-(BOOL)isMemberOfClass:classObj是否是classObj的实例

-(BOOL)respondsTosSelector:selector  类中是否有这个方法

NSClassFromString(NSString*);由字符串得到类对象

NSStringFromClass([类名 Class]);由类名得到字符串

Class rectClass= [Rectangle class];通过类名得到类对象

Class aClass =[anObject class];通过实例得到类对象

if([obj1 class]== [obj2 class])判断是不是相同类的实例

2、动态绑定

  •  在objective-c中,一个对象内否调用指定的方法不是由编译器决定而是由运行时决定,这被称作是方法的动态绑定
  •  在objective-c里,对象不调用方法,而是接收消息,消息 表达式为: [reciver message];运行时系统首先确定接收者的类型(动态类型识别),然 后根据消息名在类的方法列表里选择相依的方法执行,所 以在源代码里消息也称为选择器(selector)
  • 消息函数的作用: 首先通过第一个参数的receiver,找到它的isa 指针,然 后在isa 指向的Class 对象中使用第二个参数selector 查 找方法; 如果没有找到,就使用当前Class 对象中的新的isa 指针 到上一级的父类的Class 对象中查找; 当找到方法后,再依据receiver 的中的self 指针找到当前 的对象,调用当前对象的具体实现的方法(IMP),然后传 递参数,调用实现方法; 假如一直找到NSObject 的Class 对象,也没有找到你调 用的方法,就会报告不能识别发送消息的错误。

3、动态加载:运行时加载新类

在运行时创建一个新类,只需要3步:

  • 为 class pair分配存储空间 ,使用 objc_allocateClassPair函数
  • 增加需要的方法使用class_addMethod函数,增加实 例变量用class_addIvar
  • objc_registerClassPair函数注册这个类,以便它能被别人使用。

注意:使用这些函数请引#import <objc/runtime.h>


 




0 0