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.
可以同时运行多个任务,每个任务的启动时间是按照加入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)delay performSelector: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中如何实现多线程的同步?
线程同步是多个线程同时访问同一个资源,等待资源访问结束;线程异步是指访问资源时再空闲等待时同时访问其他资源
- @synchronized(id anObject),(最简单的方法):会自动对参数对象加锁,保证临界区内的代码线程安全
- 使用NSLock
- 原子操作是同步的一个简单地形式,他处理简单地数据类型。原子操作的优势是他们不妨碍竞争的线程。对于简单的操作,原子操作比使用锁具有更高的性能优势
- 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>