iOS面试题-常问题及答案

未完,待更新

一、必备题

1、AFN 原理

链接:AFNetworking源码——基本架构 - 简书

2、SDWebImage原理

链接:iOS利用SDWebImage图片下载缓存 - 简书

3、runtime

链接:谈谈iOS-runtime - 简书

4、runloop

5、多线程

6、block 、代理

7、MVC、MVVM

8、KVC、KVO

以上就是面试的时候问的最多的

以下是我觉得很有用的题

二、面试题

1、Objective-C如何对内存管理的?内存管理的原则是?

1、Objective-C的内存管理主要有三种方式ARC(自动内存计数)、MRC(手动内存计数)、autorelease(自动释放池)。

每个 对象都有一个引用计数器,每个新对象的计数器是1,当对象计数器减为0时,就会被销毁。

2、内存管理原则(配对原则):只要出现了new/alloc/retain,就一定配对出现一个release/autorelease

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

多线程:

好处:

1、使用线程可以把程序中占据时间长的任务放到后台去处理,如图片、视频的下载2、发挥多核处理器的优势,并发执行让系统运行的更快、更流畅,用户体验更好

缺点:

1、大量的线程降低代码的可读性

2、更多的线程需要更多的内存空间

3、当多个线程对同一个资源出现争夺的时候要注意线程安全的问题。

iOS有三种多线程编程的技术:NSThread、NSOperation、GCD

3、谈谈week

weak表其实是一个hash表,Key是所指对象的地址,Value是weak指针的地址数组,weak是弱引用,所引用对象的计数器不会+1,并在引用对象被释放的时候自动被设置为nil。通常用于解决循环引用问题。

4、 UITableview的优化方法


5、什么是 TCP连接的三次握手

 1、建立连接时,客户端发送SYN包到服务器,并进入SYN_SEND状态,等待服务器确认;

 2、服务器收到SYN包,必须确认客户的SYN,同时自己也发送一个SYN包,即SYN+ACK包,此时服务器进入SYN_RECV状态;

 3、客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK,此包发送完毕,客户端和服务器进入established状态,完成三次握手;

另一种白话理解:

第一次握手: 客户端发送网络包,服务端收到了。这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。

第二次握手:服务端发包,客户端收到了。这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。

第三次握手: 客户端发包,服务端收到了。这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。

6、Socket的实现原理及 Socket之间是如何通信的

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。建立网络通信连接至少要一对端口号(socket)。socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口

7、HTTPS实现过程

通信过程有四次握手

1、客户端发送请求,服务器返回公钥给客户端;

2、客户端生成对称加密秘钥,用公钥对其进行加密后,返回给服务器;

3、服务器收到后,利用私钥解开得到对称加密秘钥,保存;

4、之后的交互都使用对称加密后的数据进行交互。

8、自动释放池底层怎么实现?如何工作?何时创建?何时销毁?

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

1).oc是通过一种"referring counting"(引用计数)的方式来管理内存的,对象在开始分配内存(alloc)的时候引用计数为一,以后每当碰到有copy,retain的时候引用计数都会加一,每当碰到release和autorelease的时候引用计数就会减一,如果此对象的计数变为了0, 就会被系统销毁.

2). NSAutoreleasePool就是用来做引用计数的管理工作的,这个东西一般不用你管的.

3). autorelease和release没什么区别,只是引用计数减一的时机不同而已,autorelease会在对象的使用真正结束的时候才做引用计数减一.

10、怎么保证多人开发进行内存泄露的检查.

* 使用Analyze进行代码的静态分析

* 为避免不必要的麻烦, 多人开发时尽量使用ARC

11、按钮或者其它 UIView控件的事件传递的具体过程

触摸事件的传递是从父控件传递到子控件也就是UIApplication->window->寻找处理事件最合适的view

注 意: 如果父控件不能接受触摸事件,那么子控件就不可能接收到触摸事件

应用如何找到最合适的控件来处理事件?

1).首先判断主窗口(keyWindow)自己是否能接受触摸事件

2).判断触摸点是否在自己身上

3).子控件数组中从后往前遍历子控件,重复前面的两个步骤(所谓从后往前遍历子控件,就是首先查找子控件数组中最后一个元素,然后执行1、2步骤)

4).view,比如叫做fitView,那么会把这个事件交给这个fitView,再遍历这个fitView的子控件,直至没有更合适的view为止。

5).如果没有符合条件的子控件,那么就认为自己最合适处理这个事件,也就是自己是最合适的view。

UIView不能接收触摸事件的三种情况:

* 不允许交互:userInteractionEnabled = NO

* 隐藏:如果把父控件隐藏,那么子控件也会隐藏,隐藏的控件不能接受事件

* 透明度:如果设置一个控件的透明度<0.01,会直接影响子控件的透明度。alpha:0.0~0.01为透明。

注 意:默认UIImageView不能接受触摸事件,因为不允许交互,即userInteractionEnabled = NO。所以如果希望UIImageView可以交互,需要设置UIImageView的userInteractionEnabled = YES。

12、weak 和strong有什么区别?

其实就是弱应用和强引用的区别,强引用持有对象,而弱引用不持有对象,因为strong的对象会使retainCount+1,而weak的并不会。

13、把程序自己关掉和程序进入后台,远程推送的区别

1). 关掉后不执行任何代码, 不能处理事件

2). 应用程序进入后台状态不久后转入挂起状态。在这种状态下,应用程序不执行任何代码,并有可能在任意时候从内存中删除。只有当用户再次运行此应用,应用才会从挂起状态唤醒,代码得以继续执行

3).或者进入后台时开启多任务状态,保留在内存中,这样就可以执行系统允许的动作

4).远程推送是由远程服务器上的程序发送到APNS,再由APNS把消息推送至设备上的程序,当应用程序收到推送的消息会自动调用特定的方法执行事先写好的代码

14、谈谈runloop

    1、 运行循环是与线程相关的基础的一部分。运行循环是一个事件处理循环,用于调度工作并协调事件的接收。运行循环的目的是在有工作要做时保持线程忙,当没有线程时将线程放在睡眠中

    2、底层就是do-while循环

    3、runloop是来调度线程的,当线程的runloop被开启后,线程会在执行完任务后进入休眠状态,有了任务就会被唤醒去执行任务

15、runloop和线程有什么关系?

* 每条线程都有唯一的一个RunLoop对象与之对应的

* 主线程的RunLoop是自动创建并启动

* 子线程的RunLoop需要手动启动,子线程runloop是懒加载用到的时候获取

* 子线程的RunLoop创建步骤如下:

    * 获得RunLoop对象后要调用run方法来启动一个运行循环

// 启动RunLoop

[[NSRunLoop currentRunLoop] run];

    * RunLoop的其他启动方法

// 第一个参数:指定运行模式

// 第二个参数:指定RunLoop的过期时间,即:到了这个时间后RunLoop就失效了

[[NSRunLoop currentRunLoop] runMode:kCFRunLoopDefaultMode beforeDate:[NSDate distantFuture]];

* RunLoop是来管理线程的,当线程的RunLoop被开启后,线程会在执行完任务后进入休眠状态,有了任务就会被唤醒去执行任务。

* RunLoop在第一次获取时被创建,在线程结束时被销毁。

16、runloop是来做什么的?runloop和线程有什么关系?主线程默认开启了runloop么?子线程呢?

runloop的mode是用来做什么的?有几种mode?

为什么把NSTimer对象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主运行循环以后,滑动scrollview的时候NSTimer却不动了?

答:1).runloop是来管理线程的,当线程的runloop被开启后,线程会在执行完任务后进入休眠状态,有了任务就会被唤醒去执行任务。runloop和线程有什么关系,看问题15.主线程默认开启runloop,子线程需要自己去获取.

2).model:是runloop里面的模式,不同的模式下的runloop处理的事件和消息有一定的差别。

系统默认注册了5个Mode:

(1)kCFRunLoopDefaultMode: App的默认 Mode,通常主线程是在这个 Mode 下运行的。

(2)UITrackingRunLoopMode: 界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响。

(3)UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用。

(4)GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到。

(5)kCFRunLoopCommonModes: 这是一个占位的 Mode,没有实际作用。

3).nstime对象是在 NSDefaultRunLoopMode下面调用消息的,但是当我们滑动scrollview的时候,NSDefaultRunLoopMode模式就自动切换到UITrackingRunLoopMode模式下面,却不可以继续响应nstime发送的消息。所以如果想在滑动scrollview的情况下面还调用nstime的消息,我们可以把nsrunloop的模式更改为NSRunLoopCommonModes

17、NSString为什么要用copy关键字,如果用strong会有什么问题?(注意:这里没有说用strong就一定不行。使用copy和strong是看情况而定的

经常使用copy关键字原因:

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

如果改用strong关键字,可能造成什么问题?

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

举例说明:

定义一个以 strong 修饰的 array:

@property (nonatomic ,readwrite, strong)NSArray *array;

然后进行下面的操作:

NSArray *array = @[ @1, @2, @3, @4 ];

NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:array];

self.array = mutableArray;

[mutableArray removeAllObjects];

NSLog(@"%@",self.array);

[mutableArray addObjectsFromArray:array];

self.array = [mutableArray copy];

[mutableArray removeAllObjects];

NSLog(@"%@",self.array);

打印结果如下所示:

19:10:32.523 XXArrayCopyDmo[10681:713670] ()

19:10:32.524 XXArrayCopyDmo[10681:713670] (1,2,3,4)


18、 UITableview的优化方法


19、有没有用过运行时,用它都能做什么?(交换方法,创建类,给新创建的类增加方法,改变isa指针)


20、以+scheduledTimerWithTimeInterval...的方式触发的 timer,在滑动页面上的列表时,timer会暂定回调,为什么?如何解决?


这里强调一点:在主线程中以+scheduledTimerWithTimeInterval...的方式触发的timer 默认是运行在 NSDefaultRunLoopMode模式下的,当滑动页面上的列表时,进入了 UITrackingRunLoopMode模式,这时候 timer 就会停止。可以修改 timer 的运行模式为 NSRunLoopCommonModes,这样定时器就可以一直运行了。

以下是我的补充:

在子线程中通过 scheduledTimerWithTimeInterval:...方法来构建

NSTimer方法内部已经创建NSTimer 对象 ,并加入到 RunLoop 中 , 运行模式为NSDefaultRunLoopMode。

由于 Mode 有 timer 对象,所以 RunLoop 就开始监听定时器事件了,从而开始进入运行循环。

这个方法仅仅是创建 RunLoop 对象,并不会主动启动 RunLoop,需要再调用 run方法来启动

如果在主线程中通过 scheduledTimerWithTimeInterval:...方法来构

建 NSTimer,就不需要主动启动 RunLoop 对象,因为主线程的 RunLoop 对象在程序运行起来就已经被启动了


// userInfo 参数:用来给 NSTimer 的

userInfo属性赋值,userInfo是只读的,只能在构建 NSTimer对象

时赋值

[NSTimer scheduledTimerWithTimeInterval:1.0 target:self

selector:@selector(run:)    userInfo:@"ya了个hoo"

repeats:YES];


//scheduledTimer...方法创建出来 NSTimer虽然已经指定了默认模

式,但是【允许你修改模式】

[[NSRunLoop currentRunLoop] addTimer:timer

forMode:NSRunLoopCommonModes];


// 【仅在子线程】需要手动启动 RunLoop对象,进入运行循环

[[NSRunLoop currentRunLoop] run];


21、以下代码运行结果如何?

- (void)viewDidLoad

{

[super viewDidLoad];

NSLog(@"1");

dispatch_sync(dispatch_get_main_queue(), ^{

NSLog(@"2");

});

NSLog(@"3");

}

答案:输出1    原因:主线程死锁


22、 POST方法和 GET方法有那些区别?

1、url可见性:get,参数url可见;post,url参数不可见 。2、数据传输上:get,通过拼接url进行传递参数;post,通过body体传输参数。    3、缓存性:get请求是可以缓存的post请求不可以缓存 。4、后退页面的反应get请求页面后退时,不产生影响post请求页面后退时,会重新提交请求。    5、传输数据的大小get一般传输数据大小不超过2k-4k(根据浏览器不同,限制不一样,但相差不大)post请求传输数据的大小根据php.ini 配置文件设定,也可以无限大。6、安全性这个也是最不好分析的,原则上post肯定要比get安全,毕竟传输参数时url不可见,但也挡不住部分人闲的没事在那抓包玩。安全性个人觉得是没多大区别的,防君子不防小人就是这个道理。对传递的参数进行加密,其实都一样。


GET和POST还有一个重大区别

简单的说:

GET产生一个TCP数据包;POST产生两个TCP数据包。

长的说:

对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);

而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。

注意 :并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。


23、使用 block时什么情况会发生引用循环,如何解决?

一个对象中强引用了block,在block中又强引用了该对象,即双向引用,就会发生循环引用。

解决方法是将该对象使用__weak或者__block修饰符修饰之后再在block中使用。

id __weak weakSelf = self; 或者 __weak __typeof(self)weakSelf = self;. 该方法可以设置宏

id __block weakSelf = self;

或者将其中一方强制制空 xxx = nil。

检测代码中是否存在循环引用问题,可使用 Facebook 开源的一个检测工具 FBRetainCycleDetector或者在类中重写delloc方法,只要每个类调用了delloc即释放了 。

24、沙盒目录结构是怎样的?各自用于那些场景?

Application:存放程序源文件,上架前经过数字签名,上架后不可修改

Documents:常用目录,iCloud 备份目录,存放数据

Library

1).Caches:存放体积大又不需要备份的数据

2).Preference:设置目录,iCloud 会备份设置信息

3).tmp:存放临时文件,不会被备份,而且这个文件下的数据有可能随时被清除的可能


25、控制器的生命周期

就是问的 view 的生命周期,下面已经按方法执行顺序进行了排序

// 自定义控制器 view,这个方法只有实现了才会执行

- (void)loadView

{

self.view = [[UIView alloc] init];

self.view.backgroundColor = [UIColor orangeColor];

}

// view 是懒加载,只要 view 加载完毕就调用这个方法

- (void)viewDidLoad

{

[super viewDidLoad];

}

// view 即将显示

- (void)viewWillAppear:(BOOL)animated{

[super viewWillAppear:animated];

}

// view 即将开始布局子控件

- (void)viewWillLayoutSubviews{

[super viewWillLayoutSubviews];

}

// view 已经完成子控件的布局

- (void)viewDidLayoutSubviews{

[super viewDidLayoutSubviews];

}

// view 已经出现

- (void)viewDidAppear:(BOOL)animated

{

    [super viewDidAppear:animated];

}

// view 即将消失

- (void)viewWillDisappear:(BOOL)animated{

    [super viewWillDisappear:animated];

}

// view 已经消失

- (void)viewDidDisappear:(BOOL)animated{

[super viewDidDisappear:animated];

}

// 收到内存警告

- (void)didReceiveMemoryWarning{

[super didReceiveMemoryWarning];

}

// 方法已过期,即将销毁 view

- (void)viewWillUnload{

}

// 方法已过期,已经销毁 view

- (void)viewDidUnload{

}

但是我们最长常用方法:

viewDidLoad

viewWillAppear

viewWillDisappear


25、ARC是为了解决什么问题诞生的?

首先解释 ARC: automatic reference counting 自动引用计数

MRC 的缺点:

在 MRC 时代当我们要释放一个堆内存时,首先要确定指向这个堆空间的指针都被release 了

释放指针指向的堆空间,首先要确定哪些指针指向同一个堆,这些指针只能释放一次(MRC 下即谁创建,谁释放,避免重复释放)

模块化操作时,对象可能被多个模块创建和使用,不能确定最后由谁去释放

多线程操作时,不确定哪个线程最后使用完毕

综上所述,MRC 有诸多缺点,很容易造成内存泄露和坏内存的问题,这时苹果为尽量解决这个问题,从而诞生了 ARC


26、ARC下还会存在内存泄露吗?

循环引用会导致内存泄露.

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

CoreFoundation 中的对象不受 ARC 管理,需要开发者手动释放

27、什么情况使用 weak关键字,相比 assign有什么不同?

首先明白什么情况使用 weak关键字?

1.在 ARC 中,在有可能出现循环引用的时候,往往要通过让其中一端使用 weak 来解决,比如:delegate 代理属性,代理属性也可使用 assign

2.自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用 weak,自定义

3.IBOutlet 控件属性一般也使用 weak;当然,也可以使用 strong,但是建议使用 weak

weak 和 assign 的不同点

weak 策略在属性所指的对象遭到摧毁时,系统会将 weak 修饰的属性对象的指针指向 nil,在 OC 给 nil 发消息是不会有什么问题的;如果使用 assign 策略在属性所指的对象遭到摧毁时,属性对象指针还指向原来的对象,由于对象已经被销毁,这时候就产生了野指针,如果这时候在给此对象发送消息,很容造成程序奔溃

assigin 可以用于修饰非 OC 对象,而 weak 必须用于 OC 对象


28、+(void)load; +(void)initialize;有什么用处?

+(void)load;

当类对象被引入项目时, runtime 会向每一个类对象发送 load 消息。

在main函数调用前,+(void)load就已经调用完了。load 方法会在每一个类甚至分类被引入时仅调用一次,调用的顺序:父类优先于子类, 子类优先于分类。

由于 load 方法会在类被 import 时调用一次,而这时往往是改变类的行为的最佳时机,在这里可以使用例如 method swizlling 来修改原有的方法。

load 方法不会被类自动继承。

+(void)initialize;

也是在第一次使用这个类的时候会调用这个方法,也就是说 initialize 也是懒加载

总结:

在 Objective-C 中,runtime 会自动调用每个类的这两个方法

1.+load 会在类初始加载时调用

2.+initialize 会在第一次调用类的类方法或实例方法之前被调用

这两个方法是可选的,且只有在实现了它们时才会被调用

两者的共同点:两个方法都只会被调用一次

29、Foundation对象与 Core Foundation对象有什么区别

Foundation 框架是使用 OC 实现的,Core Foundation 是使用 C 实现的。

30、UIView 和CALayer的区别?

1、UIView可以响应事件,CALayer不可以,因为他们继承不同,UIView继承UIResponder,CALayer继承NSObject.

2、UIView像是一个CALayer的管理器,访问它的跟绘图和跟坐标有关的属性,例如frame,bounds等等,实际上内部都是在访问它所包含的CALayer的相关属性。

3、CALayer侧重动画,UIView显示及事件响应

链接:详解 CALayer 和 UIView 的区别和联系 - CocoaChina_让移动开发更简单


31、Category 有什么好处?为什么不能扩张属性?

答:好处:

1).比如一个类功能太庞大,可以使用category进行分散

2).创建私有方法的前向引用

-例如Person类有一个私有方法

-(void)processStr:(NSString*)str;

在viewDidLoad方法中想要使用Person的这个方法

Person* p = [[Person alloc] init];

[p precoessStr:@"str"];

这样编译不能通过,如果给Person添加了一个分类,并且在分类中声明这个方法,再在viewDidLoad方法的文件中包含这个分类,那么就不会报错,可以正常运行了。

3).添加非正式协议

正式协议需要用@protocol,@required,

@optional,@delegate来规范书写方式,而非正式协议则不需要,只要相应的delegate类直接实现就可以。但是category仅限于NSObject类的分类,不能用它的子类实现非正式协议

如:NSObject+Test分类

-(NSString)inputStr:(NSString)str;这个方法要么在NSObject+Test.m文件中实现,要么在Person.m文件中实现

//NSObject+Test.h#import<Foundation/Foundation.h>@interfaceNSObject(Test)-(NSString*)inputStr:(NSString*)str;@end//NSObject+Test.m文件#import"NSObject+test.h"@implementationNSObject(test)@end

Person类

//Person.h文件

#import<Foundation/Foundation.h>

#import"NSObject+Test.h"

@interfacePerson:NSString

@end

//Person.m文件

#import"Person.h"

@implementationPerson

-(NSString*)inputStr:(NSString*)str{

if([str isEqualToString:@"wdc"])

{

        return@"ldy";

  }

return@"error";

}

@end

在viewcontroller中

- (void)viewDidLoad {

    [superviewDidLoad];

  Person* p = [[Person alloc] init];

NSString* str = [p inputyouName:@"wdc"];

NSLog(@"%@",str);}

输出:ldy

不能扩展原因请参考:探究iOS分类(category)为什么不能直接添加属性 - lixuezhi - CSDN博客

32、简述NotificationCenter、KVC、KVO、Delegate?并说明它们之间的区别?

Notification是观察者模式的实现,KVO是观察者模式的OB-C底层实现。

NOtification通过Notifydcation addobserver和remove observer工作。

KVO是键值监听,键值观察机制,提供了观察某一属性变化的方法

KVC是键值编码,是一种间接访问对象的属性,使用字符串来标示属性(例如:setValue:forKey:) Delegate:把某个对象要做的事情委托给别的对象去做。那么别的对象就是这个对象的代理,代替它来打理要做的事。反映到程序中,首先要明确一个对象的委托方是哪个对象,委托所做的内容是什么。

33、数据持久化存储方式有哪些?以及特点?

1).plist 属性列表 最外层只能存储数组字典 里面只能存储 bool NSNumber String Data Date

2).NSUserDefault 最终也是保存成plist 系统封装了保存的路径 保存的方法

3).归档 可以对保存数据的文件 进行加密

4).sqlite  关系型数据库 以表的形式存储  FMDB是对 OC中 sqlite操作封装 的第三方库

5).coreData 是苹果封装的 对文件操作的框架 可以 以对象的形式存储 底层数据文件可以是sqlite类型 也可以是XML JSON …


34、如何对定位和分析项⽬中影响性能的地方?以及如何进行性能优化? 定位⽅法:

instruments  在iOS上进⾏性能分析的时候,⾸先考虑借助instruments这个利器分析出问题出在哪,不要凭空想象,不然你可能把精力花在了1%的问题上,最后发现其实啥都没优化,比如要查看程序哪些部分最耗时,可以使用Time Profiler,要查看内存是否泄漏了,可以使用Leaks等。关于instruments网上有很多资料料,作为一个合格iOS开发者,熟悉这个工具还是很有必要的。

优化建议:

1.⽤ARC管理理内存

* ARC(Automatic Reference Counting, 自动引用计数)和iOS5⼀一起发布,它避免了 最常见的也就是经常是由于我们忘记释放内存所造成的内存泄露露。它⾃动为你管理理 retain和release的过程,所以你就不必去⼿动⼲预了。下面是你会经常⽤来去创建一 个View的代码段: UIView *view = [[UIView alloc] init];

* // ...

* [self.view addSubview:view];

* [view release];

* 忘掉代码段结尾的release简直像记得吃饭一样简单。而ARC会自动在底层为你做这 些工作。除了帮你避免内存泄露,ARC还可以帮你提高性能,它能保证释放掉不再需要的对象的内存。

3.尽量量把views设置为完全不不透明

* 如果你有透明的Views你应该设置它们的opaque(不透明)属性为YES。例例如⼀个黑色半透明的可以设置为一个灰色不透明的View替代.原因是这会使系统⽤一个最优的方式渲染这些views。这个简单的属性在IB或者代码⾥都可以设定。

* Apple的文档对于为图片设置透明属性的描述是:

* (opaque)这个属性给渲染系统提供了一个如何处理理这个view的提示。如果设为 YES, 渲染系统就认为这个view是完全不透明的,这使得渲染系统优化一些渲染过程和提高性能。如果设置为NO,渲染系统正常地和其它内容组成这个View。默认值是 YES。

* 在相对⽐较静止的画面中,设置这个属性不会有太大影响。然而当这个view嵌在 scroll view里边,或者是一个复杂动画的一部分,不设置这个属性的话会在很⼤程度上影响app的性能。

* 换种说法,⼤家可能更好理解:只要一个视图的不透明度小于1,就会导致 blending.blending操作在iOS的图形处理器(GPU)中完成的,blending主要指的是混合像素颜色的计算。举个例子,我们把两个图层叠加在一起,如果第一个图层的有透明效果,则最终像素的颜色计算需要将第二个图层也考虑进来。这⼀过程即为Blending。为什么Blending会导致性能的损失?原因是很直观的,如果一个图层是完全不透明的,则系统直接显示该图层的颜色即可。⽽如果图层是带透明效果的,则会引入更多的计算,因为需要把下面的图层也包括进来,进行混合后颜⾊的计算。

4. 避免过于庞⼤大的XIB

* iOS5中加入的Storyboards正在快速取代XIB。然而XIB在一些场景中仍然很有用。⽐如你的app需要适应iOS5之前的设备,或者你有⼀个自定义的可重⽤的view,你就不可避免地要用到他们。

* 如果你不得不XIB的话,使他们尽量简单。尝试为每个Controller配置⼀一个单独的 XIB,尽可能把一个ViewController的view层次结构分散到单独的XIB中去。

* 需要注意的是,当你加载一个XIB的时候所有内容都被放在了内存里里,包括任何图片。如果有一个不会即刻用到的view,你这就是在浪费宝贵的内存资源了。 Storyboards就是另一码事儿了,storyboard仅在需要时实例例化一个viewcontroller.

* 当你加载⼀个引用了了图片或者声音资源的nib时,nib加载代码会把图⽚片和声音文件写进内存。在OS X中,图片和声音资源被缓存在named cache中以便将来用到时获取。在iOS中,仅图片资源会被存进named caches。取决于你所在的平台,使⽤用 NSImage 或UIImage 的imageNamed:方法来获取图片资源。

5. 不要阻塞主线程

* 永远不要使主线程承担过多。因为UIKit在主线程上做所有工作,渲染,管理理触摸反 应回应输入等都需要在它上面完成。一直使用主线程的风险就是如果你的代码真的block了主线程,你的app会失去反应

* 大部分阻碍主进程的情形是你的app在做一些牵涉到读写外部资源的I/O操作,比如 存储或者网络。或者使用像 AFNetworking这样的框架来异步地做这些操作。如果你需要做其它类型的需要耗费巨⼤资源的操作(⽐如时间敏感的计算或者存储读写)那就用 Grand Central Dispatch,或者 NSOperation 和 NSOperationQueues.你可以使用NSURLConnection异步地做⽹络操作: + (void)sendAsynchronousRequest: (NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler

6. 在ImageViews中调整图片大小

* 如果要在UIImageView中显示一个来自bundle的图片,你应保证图片的⼤小和


UIImageView的⼤小相同。在运行中缩放图片是很耗费资源的,特别是UIImageView 嵌套在UIScrollView中的情况下。

* 如果图片是从远端服务加载的你不能控制图⽚大⼩,比如在下载前调整到合适⼤小 的话,你可以在下载完成后,最好是用background thread,缩放一次,然后在 UIImageView中使用缩放后的图片。

7. 选择正确的Collection 学会选择对业务场景最合适的类或者对象是写出能效⾼高的代码的基础。当处理理 collections时这句话尤其正确。

Apple有⼀一个 Collections Programming Topics 的文档详尽介绍了可用的classes间 的差别和你该在哪些场景中使用它们。这对于任何使用collections的人来说是一个必 读的文档。

呵呵,我就知道你因为太长没看...这是一些常见collection的总结:

* Arrays: 有序的一组值。使用index来lookup很快,使用value lookup很慢, 插⼊/删 除很慢。

* Dictionaries: 存储键值对。 ⽤键来查找比较快。

* Sets: 无序的⼀组值。用值来查找很快,插入/删除很快。

8. 打开gzip压缩

* ⼤量app依赖于远端资源和第三⽅API,你可能会开发一个需要从远端下载XML, JSON, HTML或者其它格式的app。

* 问题是我们的目标是移动设备,因此你就不能指望⽹络状况有多好。一个⽤户现在还在edge网络,下一分钟可能就切换到了了3G。不论什么场景,你肯定不想让你的⽤户等太长时间。

* 减⼩文档的一个方式就是在服务端和你的app中打开gzip。这对于文字这种能有更⾼压缩率的数据来说会有更显著的效用。好消息是,iOS已经在NSURLConnection中默 认支持了gzip压缩,当然AFNetworking这些基于它的框架亦然。像Google App Engine这些云服务提供者也已经⽀支持了了压缩输出。


35、iOS app启动如何优化?

1. 查看启动时间,对这块优化。通过在 Xcode 中 Edit scheme -> Run -> Auguments 将环境变量量 DYLD_PRINT_STATISTICS 设为 1,在控制台看到main()函数之前的启动时间。

2. 分解优化目标分步达到优化⽬的

1). 耗时操作异步处理

2). 如果启动流程依赖网络请求回来才能继续,那么需要考虑网络极差情况下的启动速度

3). 如果APP有loading广告页并且对分辨率的要求较⾼,请尝试做缓存吧

4). 主⻚面Controller中的viewDidLoad和viewWillAppear⽅法中尽量少做事情

5). 排查清理项目中未使用到的静态类库以及Framework,因为在编译阶段系统会把静态类库加载到内存里

6). 删减合并⼀一些OC类,删减没有用到或者可以不用的静态变量、方法等

7). 轻量化+load方法中的内容,可延迟到+initialize中,因为load在mian()函数前已经加载过了,runtime机制


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

答案:

好处:

1.使⽤用线程可以把占据时间⻓的程序中的任务放到后台去处理

2.⽤户界面可以更加吸引人,比如用户点击了一个按钮去触发某些事件的处理, 可以弹出一个进度条来显示处理的进度

3.程序的运行速度可能加快

4·在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有 用了。

缺点:

1.如果有⼤量的线程,会影响性能,因为操作系统需要在它们之间切换。创建更多的线程需要更更多的内存空间。

3.线程的中⽌止需要考虑其对程序运⾏行行的影响。

4.通常块模型数据是在多个线程间共享的,需要防⽌止线程死锁情况的发⽣生。 实现多线程的⽅方法:

37、NSOPrationQueue 与GCD分别在什么情况下更合适使用?

1).GCD是底层的C语言构成的API,而NSOperationQueue是基于GCD的OC封装。

2).GCD支持FIFO队列,NSOperationQueue可以方便设置执行顺序,设置最大的并发数量。

3).NSOperationQueue可以方便地设置operation之间的依赖关系,GCD则需要更多的代码。

4).NSOperationQueue支持KVO,可以检测operation是否正在执行(isExecuted),是否结束(isFinished),是否取消(isCanceled)。

5).GCD的执行速度比NSOperationQueue快。

使用场合:

任务之间不太相互依赖:GCD;

任务之间依赖或者监听任务的执行情况:NSOperationQueue


38、问:什么时候需要使用自动释放池?

官方解释:基本分为如下三点

1、当我们需要创建大量的临时变量的时候,可以通过@autoreleasepool 来减少内存峰值。

2、创建了新的线程执行Cocoa调用。

3、如果您的应用程序或线程是长期存在的,并且可能会生成大量自动释放的对象,那么您应该定期清空并创建自动释放池(就像UIKit在主线程上所做的那样);否则,自动释放的对象会累积,内存占用也会增加。但是,如果创建的线程不进行Cocoa调用,则不需要创建自动释放池。

追问:为什么会减少内存峰值?

答:借用YYImage的代码打个比方。

比如业务需要在一个代码块中需要创建大量临时变量,或临时变量足够大,占用了很多内存,可以在临时变量使用完以后就立即释放掉,在ARC的环境下只能通过自动释放池实现。

if ([UIDevice currentDevice].isSimulator) {

        @autoreleasepool {

            NSString *outPath = [NSString stringWithFormat:@"%@ermilio.gif.png",IMAGE_OUTPUT_DIR];

            NSData *outData = UIImagePNGRepresentation([UIImage imageWithData:gif]);

            [outData writeToFile:outPath atomically:YES];

            [gif writeToFile:[NSString stringWithFormat:@"%@ermilio.gif",IMAGE_OUTPUT_DIR] atomically:YES];

        }

        @autoreleasepool {

            NSString *outPath = [NSString stringWithFormat:@"%@ermilio.apng.png",IMAGE_OUTPUT_DIR];

            NSData *outData = UIImagePNGRepresentation([UIImage imageWithData:apng]);

            [outData writeToFile:outPath atomically:YES];

            [apng writeToFile:[NSString stringWithFormat:@"%@ermilio.png",IMAGE_OUTPUT_DIR] atomically:YES];

        }

        @autoreleasepool {

            NSString *outPath = [NSString stringWithFormat:@"%@ermilio_q85.webp.png",IMAGE_OUTPUT_DIR];

            NSData *outData = UIImagePNGRepresentation([YYImageDecoder decodeImage:webp_q85 scale:1]);

            [outData writeToFile:outPath atomically:YES];

            [webp_q85 writeToFile:[NSString stringWithFormat:@"%@ermilio_q85.webp",IMAGE_OUTPUT_DIR] atomically:YES];

        }

}

再比如在循环的场景下,如果创建大量的临时变量,会使内存峰值持续增加,加入自动释放池以后,在每次循环结束时,超出自动释放池的作用域,使得内部的大量临时变量被释放,从而大大降低了内存的使用。

for (int i = 0; i < count; i++) {

        @autoreleasepool {

            id imageSrc = _images[i];

            NSDictionary *frameProperty = NULL;

            if (_type == YYImageTypeGIF && count > 1) {

                frameProperty = @{(NSString *)kCGImagePropertyGIFDictionary : @{(NSString *) kCGImagePropertyGIFDelayTime:_durations[i]}};

            } else {

                frameProperty = @{(id)kCGImageDestinationLossyCompressionQuality : @(_quality)};

            }

}

上述这几种情况如果没必要就别这么写,毕竟创建自动释放池也需要耗费内存。



40、iOS面试题:有了线程,你觉得为什么还要有runloop?,runloop和线程有什么关系?

解析:关于为什么要,我觉得runloop是来管理(调度)线程的,当线程的runloop被开启后,线程会在执行完任务后进入休眠状态,有了任务就会被唤醒去执行任务。

关于这两者的更多关系:

* runloop与线程是一一对应的,一个runloop对应一个核心的线程,为什么说是核心的,是因为runloop是可以嵌套的,但是核心的只能有一个,他们的关系保存在一个全局的字典里。

* runloop在第一次获取时被创建,在线程结束时被销毁。

* 对于主线程来说,runloop在程序一启动就默认创建好了。

* 对于子线程来说,runloop是懒加载的,只有当我们使用的时候才会创建,所以在子线程用定时器要注意:确保子线程的runloop被创建,不然定时器不会回调。

41、iOS面试题:NSOperation 与 GCD 的主要区别?

• 1. GCD 的核心是 C 语言写的系统服务,执行和操作简单高效,因此 NSOperation 底层也通过 GCD 实现,换个说法就是 NSOperation 是对 GCD 更高层次的抽象,这是他们之间最本质的区别。因此如果希望自定义任务,建议使用 NSOperation;

• 2. 依赖关系,NSOperation 可以设置两个 NSOperation 之间的依赖,第二个任务依赖于第一个任务完成执行,GCD 无法设置依赖关系,不过可以通过dispatch_barrier_async来实现这种效果;

• 3. KVO(键值对观察),NSOperation 和容易判断 Operation 当前的状态(是否执行,是否取消),对此 GCD 无法通过 KVO 进行判断;

• 4. 优先级,NSOperation 可以设置自身的优先级,但是优先级高的不一定先执行,GCD 只能设置队列的优先级,无法在执行的 block 设置优先级;

• 5. 继承,NSOperation 是一个抽象类,实际开发中常用的两个类是 NSInvocationOperation 和 NSBlockOperation ,同样我们可以自定义 NSOperation,GCD 执行任务可以自由组装,没有继承那么高的代码复用度;

• 6. 效率,直接使用 GCD 效率确实会更高效,NSOperation 会多一点开销,但是通过 NSOperation 可以获得依赖,优先级,继承,键值对观察这些优势,相对于多的那么一点开销确实很划算,鱼和熊掌不可得兼,取舍在于开发者自己;


42、你是否接触过OC中的反射机制?简单聊一下概念和使用、OC

OC的反射是基于其Runtime实现的。

反射,一般表现在字符串和Class转换,字符串和内部方法转换,字符串和属性的转换(取值和赋值)。


• class反射

  1、通过类名的字符串形式实例化对象

Class class NSClassFromString@(@"student"); Student *stu = [[class alloc ]init];

  2、将类名变为字符串

Class class =[Student class];

NSString *className = NSStringFromClass(class);

• SEL的反射

  1、通过方法的字符串形式实例化方法

SEL selector = NSSelectorFromClass(@"setName"); [stu performSelector:selector withObject:@"Mike"];

2、将方法变为字符串

NSStringFomrSelector(@selector*(setName:))

反射机制使用技巧 :假设有一天公司产品要实现一个需求:根据后台推送过来的数据,进行动态页面跳转,跳转到页面后根据返回到数据执行对应的操作。 遇到这样奇葩的需求,我们当然可以问产品都有哪些情况执行哪些方法,然后写一大堆if else判断或switch判断。 但是这种方法实现起来太low了,而且不够灵活,假设后续版本需求变了,还要往其他已有页面中跳转,这不就傻眼了吗.... 这种情况反射机制就派上用场了,我们可以用反射机制动态的创建类并执行方法。当然也可以通过runtime来实现这个功能,但是我们当前需求反射机制已经足够满足需求了,如果遇到更加复杂的需求可以考虑用runtime来实现。 这时候就需要和后台配合了,我们首先需要和后台商量好返回的数据结构,以及数据格式、类型等,返回后我们按照和后台约定的格式,根据后台返回的信息,直接进行反射和调用即可。


43、为什么NSAarray用copy修饰,而NSMutableArray不能用copy修饰

因为NSMutableArray用copy修饰,所得到的实际是NSArray,在删除、添加等操作会crash。

44、如何访问并修改一个类的私有属性?

• kvc

• 通过runtime访问并修改私有属性

45、在一个对象的方法里面:self.name= “object”;和 name =”object” 有什么不同吗?

答:self.name =”object”:会调用对象的setName()方法;

name = “object”:会直接把object赋值给当前对象的name属性。

46、iOS block内为什么要使用strongSelf

先摘抄一段来自AFNetworking的一段代码:

__weak __typeof(self)weakSelf = self;

AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {

    __strong __typeof(weakSelf)strongSelf = weakSelf;

    strongSelf.networkReachabilityStatus = status;

    if (strongSelf.networkReachabilityStatusBlock) {

        strongSelf.networkReachabilityStatusBlock(status);

    }

};

我们知道使用weakSelf的作用是为了防止强循环引用, 产生不必要的内存泄漏问题. 但是为什么在block内部还要重新转成strongSelf.

究其原因, 因为在block内部的weakSelf有可能为为self或者为nil (比如当前界面正在加载网络数据, 而此时用户关闭了该界面). 这样在某些情况下代码会崩溃. 所以为了让self不为nil, 我们在block内部将weakSelf转成strongSelf. 因为strongSelf在block内部属于局部变量了,当block结束时, 该strongSelf变量也会被自动释放., 既避免了循环引用, 又让self在block内部不为nil. 

故为了保证self在block执行过程里一直存在,对他强引用strongSelf


47、fmdb框架

答:数据库框架,对sqllite的数据操作进行了封装,使用着可把精力都放在sql语句上面。

FMDB同时兼容ARC和非ARC工程,会自动根据工程配置来调整相关的内存管理代码。 FMDB常用类: FMDatabase:一个单一的SQLite数据库,用于执行SQL语句。 FMResultSet:执行一个FMDatabase结果集。                              FMDatabaseQueue:在多个线程中执行查询和更新时会用到这个类。内部是同步串行队列来保证数据库访问的安全性

1,轻量级,灵活。不消耗太多性能

2,FMDB将Ç语言的iOS的系统的SQLite的数据库的操作代码用OC进行封装,面向对象,容易理解和使用

注意:补补自己的sql,外键、索引、多表查询等

48、TCP和UDP的区别

答:他们类似申通和韵达快递,是传输层协议。

TCP可靠的数据流传输,而UDP提供的是不可靠的数据流传输。

简单的说,TCP注重数据安全,而UDP数据传输快点,但安全性一般

48、http和scoket通信的区别。

答:由于通常情况下Socket连接就是TCP连接,因此Socket连接一旦建立,通信双方即可开始相互发送数据内容,直到双方连接断开。但在实际网络应用中,客户端到服务器之间的通信往往需要穿越多个中间节点,例如路由器、网关、防火墙等,大部分防火墙默认会关闭长时间处于非活跃状态的连接而导致 Socket 连接断连,因此需要通过轮询告诉网络,该连接处于活跃状态。

  而HTTP连接使用的是“请求—响应”的方式即三次握手,不仅在请求时需要先建立连接,而且需要客户端向服务器发出请求后,服务器端才能回复数据。

很多情况下,需要服务器端主动向客户端推送数据,保持客户端与服务器数据的实时与同步。此时若双方建立的是Socket连接,服务器就可以直接将数据传送给客户端;若双方建立的是HTTP连接,则服务器需要等到客户端发送一次请求后才能将数据传回给客户端,因此,客户端定时向服务器端发送连接请求,不仅可以保持在线,同时也是在“询问”服务器是否有新的数据,如果有就将数据传给客户端。

区别简述:

  1)http是一种协议,socket是一种编程接口,主要包括TCP协议和UDP协议;

  2)http和TCP/UDP是两个不同层上的的协议。http是应用层的协议,TCP/UDP是传输层的协议,http是在TCP/UDP之上的协议,http协议使用了TCP/UDP,http更加高级一点但是没有很好的灵活性。也就是http使用起来比TCP/UDP要简单,只需要遵循规范就可以进行网络通信了。

49、GCD常见的死锁问题之一

问:打印的结果是什么?结果1:因为执行到这个同步线程这里就死锁了;结果2:1、3、2 ;

原因分析:因为dispatch_async()被放到了主队列的末尾执行,即结束了viewDidLoad 主队列中的内容才执行这个;首先,无论是同步sync还是异步async 都是调用一个block,这个block会被放到指定的队列(queue)的队尾等待执行(重点:队尾等待执行);至于block中是并行执行还是串行执行那就和dispatch_sync中的参数指定的queue是并行还是串行有关;不同的是sync地等到block有结果了才能进行下一步操作也就是nslog(@"3");而async不需要,可以和nslog(@"3")同时执行;同步(sync) 操作,它会阻塞当前线程并等待 Block 中的任务执行完毕,然后当前线程才会继续往下运行;异步(async)操作,它不会阻塞当前的线程,会一起执行任务;viewDidLoad操作是由上到下一步一步往下执行的,dispatch_sync提交一个打印任务NSLog(@”2”)到主线程关联的串行队列中,主线程关联的串行队列现在有一个viewDidLoad任务,打印任务NSLog(@”2”)排在viewDidLoad后面,队列FIFO(先进先出)的原则,打印任务NSLog(@”2”);想要得到执行必须等到viewDidLoad执行完毕后才能得到执行,但是viewDidLoad想要执行完毕必须要等打印任务NSLog(@”2”)执行完毕,所以就卡死在这了。

50、Category(类别)、 Extension(扩展)和继承的区别

区别:

1. 分类有名字,类扩展没有分类名字,是⼀一种特殊的分类。

2. 分类只能扩展⽅方法(属性仅仅是声明,并没真正实现),类扩展可以扩展属性、成 员变量量和⽅方法。

3. 继承可以增加,修改或者删除⽅方法,并且可以增加属性。

51、为什么代理要用weak?block和代理的区别?他们哪个性能好,为什么?

通过weak打破循环引⽤用。 代理和block的区别:代理和block的共同特性是回调机制。不同的是代理的方法比较多,比较注重过程。block代码比较集中、简洁,比较注重结果;代理的运行成本要低于block的运行成本,block的出站需要从栈内存拷⻉到堆内存。公共接⼝比较多时,用代理解耦;简单回调和异步线程中使用block。

52、在一个UIView上放一个按钮,如图1,请问B部分点击有反应吗?


图1

答:没反应,原因就是按钮在UIView中超出了范围,超出范围的部分当然点击不到了这主要与iOS的事件分发机制(hit-Testing)有关。hit-Testing作用是找出点击的是哪一个view,UIView中有两个方法来确定hit-TestView

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;//如果在当前view中,就返回该view

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;//判断触摸点是否在某个UIView中

备注:父 View 的 hitTest:withEvent 中,会调用子 View 的 pointInside:withEvent:,根据后者的结果判断点击的点是否在当前的 子 view 中。

解决方案在UIView中重写hitTest:withEvent:方法

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {

    UIView *view = [super hitTest:point withEvent:event];

    CGPoint tempPoint = [self.btn convertPoint:point fromView:self];

    if ([self.btn pointInside:tempPoint withEvent:event]) {

        return self.btn;

    }

    return view;

}


53、用weak修饰的UIView *a,addSubview添加到另一个UIView  *b上,a能显示出来吗?

答:不能,weak 修饰的 属性,init 不会 使引用计数 + 1,strong修饰的 属性,init 会使 引用计数 + 1,addsubView 方法 会把 控件 添加到 subView 中,而subView是copy修饰,引用+1。所以:weak 修饰的控件 没有加到 VIew上 引用计数 为0,就被释放了

但是我们看到系统xib或者storyboard拖的属性能显示出来,那是因为系统内部对它所绑定的属性做了强引用操作

54、在block中打破循环引用有几种方法?

1、使用weak

2、在把使用的变量,在使用完后=nil;

55、链表与数组的区别?

(1)存储形式:数组是一块连续的空间,声明时就要确定长度。链表是一块可不连续的动态空间,长度可变,每个节点要保存相邻结点指针;

(2)数据查找:数组的线性查找速度快,查找操作直接使用偏移地址。链表需要按顺序检索结点,效率低;

(3)数据插入或删除:链表可以快速插入和删除结点,而数组则可能需要大量数据移动;

(4)越界问题:链表不存在越界问题,数组有越界问题。

数组便于查询,链表便于插入删除。数组节省空间但是长度固定,链表虽然变长但是占了更多的存储空间。

链接:https://blog.csdn.net/dangzhangjing97/article/details/81699050

56、NSArray是放在堆还是栈里的?

1、这个的分情况,


57、  __weak与__block修饰符有什么区别?

__block:特点 1.__block对象在block中是可以被修改、重新赋值的。 2.__block对象在block中不会被block强引用一次,从而不会出现循环引用问题。3.__block不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型。

__weak:特点 1.__weak只能在ARC模式下使用,也只能修饰对象(NSString),不能修饰基本数据类型(int)。2、weak 关键字的作用弱引用,所引用对象的计数器不会加一,并在引用对象被释放的时候自动被设置为 nil。所以可以避免循环引用。

区别:

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

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

3.__block对象可以在block中被重新赋值,__weak不可以。

4.__block对象在ARC下可能会导致循环引用,非ARC下会避免循环引用,__weak只在ARC下使用,

58、ios的内存分区情况,这个得了解要不然问你底层就很懵逼

内存分区:栈区,堆区,文字常量区,全局区,代码区

1、栈区(stack):

-栈区(stack)由编译器自动分配释放 ,存放方法(函数)的参数值, 局部变量的值等,栈是向低地址扩展的数据结构,是一块连续的内存的区域。即栈顶的地址和栈的最大容量是系统预先规定好的(2M)

- 它是后入先出(LIFO)机制:比如瓶子里面装东西 最上面是最后放的,最先拿出来

-栈区(stack)由编译器自动分配释放 ,存放方法(函数)的参数值, 局部变量的值等

- 程序猿不需要管理栈区变量的内存;

2、堆区(heap):

-堆区(heap)一般由程序员分配释放, 若程序员不释放,程序结束时由OS回收,向高地址扩展的数据结构,是不连续的内存区域,从而堆获得的空间比较灵活

-ios初始化方法以 new, alloc, retain,copy 开头都是在堆区;

-- 它是先入先出(FIFO)机制:比如隧道,先进先出

-需要程序猿管理内存;

-ARC的内存的管理,是编译器再编译的时候自动添加 retain、release、autorelease;

3、全局区/静态区(static):

包括两个部分:未初始化过 、初始化过;也就是说,(全局区/静态区)在内存中是放在一起的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域;

4、常量区:常量字符串就是放在这里的。 程序结束后由系统释放 ;

5、代码区: 存放App代码;

堆栈区别:1.管理方式不同 2.空间大小不同 3.能否产生碎片 4.生长方向不同 5.分配方式不同 6.分配效率不同;

详细解释:

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

2.空间大小:一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是2M。当然,我们可以修改: 打开工程,依次操作菜单如下:Project->Setting->Link,在Category 中选中Output,然后在Reserve中设定堆栈的最大值和commit。 注意:reserve最小值为4Byte;commit是保留在虚拟内存的页文件里面,它设置的较大会使栈开辟较大的值,可能增加内存的开销和启动时间。

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

4.生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。

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

6.分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的。例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。

59、KVC的底层实现?

当一个对象调用setValue方法时,方法内部会做一下操作:

1、检查存在相应key的set方法,如果存在,就调用set方法

2、如果set方法不存在,就会查找与key相同名称并且带下划线成员属性,如果有,则直接给成员属性赋值

3、如果没有找到_key,就会啊查找相同名称的属性key,如果有就直接赋值

4、如果还是没找到,则调用valueForUndefinedKey:和setValue:forUndefinedKey:方法

这些方法的默认实现都是抛出异常,我们可以根据要重写它们

60、KVO的底层实现?

- kvo基于runtime机制实现

-使用了isa混写(isa-swizzling),当一个对象(假设是Person对象)的属性值(Person的属性age)发生改变时,系统会自动生成一个类,继承Person,NSKVONotifying_Person,在这个类的setAge方法里面,调用[super setAge:age]、[self willChangeValueForKey:@"age"]和[self didChangeValueForKey:@"age"],而这2个方法内部会主动调用监听者内部的-(void)observeValueForKeyPath这个方法。

-当你观察一个对象时,一个新的类会动态被创建。这个类继承自该对象的原本的类,并重写了被观察属性的 setter方法。自然,重写的 setter方法会负责在调用原 setter方法之前和之后,通知所有观察对象值的更改。最后把这个对象的 isa指针 ( isa 指针告诉 Runtime 系统这个对象的类是什么 ) 指向这个新创建的子类,对象就神奇的变成了新创建的子类的实例。

-想要看到NSKVONotifying_Person很简单,在self.person.age=20;这个打断点,在调试区域就能看到_person->NSObject->isa=(Class)NSKVONotifying_Person

61、ios推送原理

APNS:服务器推送给苹果服务器,苹果服务器推送给苹果设备,苹果设置推送给app

苹果通过uuid和推送证书生成deviceToken,这个deviceToken是唯一的,作用 确定是哪个手机哪个app,

62、进程是什么?线程是什么?二者有什么区别和联系?

答:一个程序至少一个进程,一个进程至少有一个线程;

进程:一个程序的一次运行,在执行过程中拥有独立的内存单元,而多个线程共享一块内存

线程:线程是指进程内的一个执行单元

联系:线程是进程的基本组成单元

区别:

(1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位

(2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行

(3)拥有资源:进程是拥有资源的一个独立单元,线程不拥有系统资源,但可以访问隶属于进程的资源

(4)系统开销:在创建或撤销进程时,由于系统都要为之分配和回收资源,导致系统开销明显大于创建或撤销线程的开销。比如操作系统有多个软件在运行(QQ、office、音乐等),这些都是一个个进程,而每个进程里又有好多线程(比如QQ、你可以同时聊天,发送文件等)

63、如何追踪app崩溃率,如何解决线上闪退?

当iOS设备上的App应用闪退时,操作系统会生成一个crash日志,保存在设备上。crash日志上有很多有用的信息,比如每个正在执行线程的完整堆栈跟踪信息和内存映像,这样就能够通过解析这些信息进而定位crash发生时的代码逻辑,从而找到App闪退的原因。通常来说,crash产生来源于两种问题:违反iOS系统规则导致的crash和App代码逻辑BUG导致的crash,下面分别对他们进行分析。

违反iOS系统规则产生crash的三种类型:

(1) 内存报警闪退

当iOS检测到内存过低时,它的VM系统会发出低内存警告通知,尝试回收一些内存;如果情况没有得到足够的改善,iOS会终止后台应用以回收更多内存;最后,如果内存还是不足,那么正在运行的应用可能会被终止掉。在Debug模式下,可以主动将客户端执行的动作逻辑写入一个log文件中,这样程序童鞋可以将内存预警的逻辑写入该log文件,当发生如下截图中的内存报警时,就是提醒当前客户端性能内存吃紧,可以通过Instruments工具中的Allocations 和 Leaks模块库来发现内存分配问题和内存泄漏问题。

(2) 响应超时

当应用程序对一些特定的事件(比如启动、挂起、恢复、结束)响应不及时,苹果的Watchdog机制会把应用程序干掉,并生成一份相应的crash日志。这些事件与下列UIApplicationDelegate方法相对应,当遇到Watchdog日志时,可以检查上图中的几个方法是否有比较重的阻塞UI的动作。

(3) 用户强制退出

一看到“用户强制退出”,首先可能想到的双击Home键,然后关闭应用程序。不过这种场景一般是不会产生crash日志的,因为双击Home键后,所有的应用程序都处于后台状态,而iOS随时都有可能关闭后台进程,当应用阻塞界面并停止响应时这种场景才会产生crash日志。这里指的“用户强制退出”场景,是稍微比较复杂点的操作:先按住电源键,直到出现“滑动关机”的界面时,再按住Home键,这时候当前应用程序会被终止掉,并且产生一份相应事件的crash日志。

常见的崩溃原因基本都是代码逻辑问题或资源问题,比如数组越界,访问野指针或者资源不存在,或资源大小写错误等。

线上Bug:项目使用了腾讯bugly,因此会有崩溃日志,通过解析dYSM可以直接定位到大部分bug崩溃之处。解决线上bug需要从主干拉一个新的分支,解决bug并测试通过后,再合并到主干,然后上线。若是多团队开发,可以将fix bug分支与其他团队最近要上线的分支集成,然后集成测试再上线。

测试Bug:根据测试所反馈的bug描述,若语义不清晰,则直接找到提bug人,操作给开发人员看,最好是可以bug复现。解决bug时,若能根据描述直接定位bug出错之处,则好处理;若无法直观定位,则根据bug类型分几种处理方式,比如崩溃的bug可以通过instruments来检测、数据显示错误的bug,则需要阅读代码一步步查看逻辑哪里写错

64、队列和栈有什么区别?

答:队列和栈是两种不同的数据容器,从“数据结构”的角度看,它们都是线性结构,即数据元素之间的关系相同。

队列是一种先进先出(FIFO)的数据结构,它在两端进行操作,一端进入队列操作,一端进行出队列操作

栈是一种先进后出(LIFO)的数据结构,它只能在栈顶进行操作,入栈和出栈都在栈顶操作

65、什么是死锁?死锁的4个必要条件?如何避免死锁?

答:线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行。比如你需要你兄弟的一样东西去完成一个任务 ,他不能给。同时你兄弟需要你的一样东西去完成一个任务,你不能给。因此你们两谁也无法完成任务。

1、 互斥条件:进程对于所分配到的资源具有排它性,即资源不能被共享,只能由一个进程使用(一个资源只能被一个进程占用,直到被该方法进程释放 。)

2、请求和保持条件:一个进程因请求被占用资源而发生阻塞时,对已获得的资源保持不放。

3、 非剥夺条件:任何一个资源在没被该进程释放之前,任何其进程都无法对他剥夺占用。

4、循环等待条件:当发生死锁时,所等待的进程必定会形成一个环路(类似于死循环),造成永久阻塞。

以上这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。线程死锁和进程死锁都一样

打破互斥条件:改造独占性资源为虚拟资源,大部分资源已无法改造。

打破不可抢占条件:当一进程占有一独占性资源后又申请一独占性资源而无法满足,则退出原占有的资源。

打破占有且申请条件:采用资源预先分配策略,即进程运行前申请全部资源,满足则运行,不然就等待,这样就不会占有且申请。

打破循环等待条件:实现资源有序分配策略,对所有设备实现分类编号,所有进程只能采用按序号递增的形式申请资源。

死锁的处理:鸵鸟策略、预防策略、避免策略、检测与解除死锁

66、以下代码死锁了怎么解决?

-(void)methodA{  [_loack lock];  [self methodB];  [_lock unlock];}

-(void)methodB{  [_lock lock];  //操作逻辑;  [_lock unlock];}

NSLock是非递归锁,当同一线程重复获取同一非递归锁时,就会发生死锁

解决办法:我们可以用NSRecursiveLock或者@synchronized替代NSLock因为NSRecursiveLock或者@synchronized都是递归锁,递归锁:它允许同一线程多次加锁,而不会造成死锁。

67、int 和NSIntger有什么区别?

NSInteger会根据系统的位数(32or64)自动选择int的最大数值(int or long)

68、怎么防止反编译?

1、本地数据加密:NSUserDefaults,sqlite储存文件数据加密,保护账号和关键字信息

2、URL编码加密:对程序中出现的URL进行编码加密,防止URL被静态分析

3、网络传输数据加密:对客户端传输数据提供加密方案,有效防止通过网络接口的拦截获取数据

4、方法体,方法名高级混淆:对应用程序的方法名和方法体进行混淆,保证源码被逆向后无法解析代码

5、程序结构混排加密:对应用程序逻辑结构进行打乱混排,保证源码可读性降到最低

69、做过的项目是否涉及网络访问功能,使用什么对象完成网络功能?

ASIHTTPRequest 与NSURLConnection

对应第三方框架 ASI  AFN

70、MRC和ARC有什么区别?


71、工厂模式是什么?什么是抽象工厂?

工厂模式:专门定义一个类来负责创建其他类的实例,被创建的实例通常具有共同的父类。

抽象工厂:抽象工厂提供一个固定的接口,用于创建一系列有关联或相依存的对象,而不必指定其具体类或其创建的细节。客户端与从工厂得到的具体对象之间没有耦合

意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。比如创建个水果类,子类可以是香蕉、橘子、苹果

优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。

缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

链接:https://blog.csdn.net/shihuboke/article/details/73921535

72、看以下代码

NSMutableString *string = [NSMutableString stringWithString: @"origin"];

//copy

NSString *stringCopy = [string copy];

NSMutableString *mStringCopy = [string copy];

NSMutableString *stringMCopy = [string mutableCopy];

//change value

[mStringCopy appendString:@"mm"]; //这行代码会crash

[string appendString:@" origion!"];

[stringMCopy appendString:@"!!"];

运行以上代码,会在第7行crash,原因就是 copy 返回的对象是 immutable 对象,违反了我们上述中的原则。 注释第7行后再运行,查看内存,发现 string、stringCopy、stringMCopy 三个对象的内存地址都不一样,说明此时都是做内容拷贝。


图1

73、assign修饰对象会怎么样?

会造成野指针,导致崩溃。首先我们需要明确,对象的内存一般被分配到堆上,基本数据类型和oc数据类型一本被分配在栈上。

如果用assign修饰对象,当对象释放后(因为不存在强引用,离开作用域对象内存可能被回收),指针的地址还是存在的,也就是说指针并没有被置为nil,下次再访问该对象就会造成野指针异常。对象时分配在堆上的,堆上的内存由程序员手动释放。

assign修饰基本数据类型或OC数据类型,因为基本数据类型是分配在栈上的,由系统分配和释放,所以不会造成野指针。

74、OC的野指针是什么,常见的情况有哪些,如何避免?

1、如果一个指针先前指向一个对象,但这个对象随后被释放了,如果该指针没有做任何的修改,导致仍然指向着那块内存地址(被释放后的内存),则该指针已成为了野指针。

比如:Student *stu = [[Student alloc] init];

[stu setAge:10];

[stu release];这里已经释放内存

[stu setAge:10];---》报错

如果改动一下代码,就不会报错[stu release]; (stu = nil; 这里是重点) [stu setAge:10]; //消息是无法发送出去的,不会造成任何的影响,当然也不会报错。

常见的野指针:1、代理用assign修饰 2、assign修饰对象 3、创建的对象提前释放了,然后再去拿着这个指针操作这个对象  4、2个指针指向同一个对象,然后其中一个指针把对象释放了,另一个指针还指着这个对象,然后这个指针就是野指针了。比如:Student *stu = [[Student alloc] init];  self.xxx=stu;  [stu rease];


75、iOS 空指针 、野指针、 僵尸对象是什么意思?

空指针:没有指向任何内存地址的指针,比如被赋值为nil的指针,在没有被具体初始化之前,为nil。

野指针:指向垃圾内存(对象被释放了的内存或者或者不可用的内存)的指针

僵尸对象:已经被释放的对象。如果在程序中再度使用该对象,一般会出现如下报错:

unrecognized selector sent to instance

76、内存泄露怎么检测?

内存泄露:是指申请的内存空间使用完毕之后未回收

一次内存泄露危害可以忽略,但若一直泄漏,无论有多少内存,迟早都会被占用光,最终导致程序crash。(因此,开发中我们要尽量避免内存泄漏的出现)

第一种:静态分析方法(Analyze)

第二种:动态分析方法(Instrument工具库里的Leaks)

目前,在ARC环境下,导致内存泄漏的根本原因是代码中存在循环引用,从而导致一些内存无法释放,最终导致dealloc()方法无法被调用。所以你只要看你的ViewController是否调用dealloc()方法,基本就可以断定是否有内存泄露了

77、Assets里的图片与Resoure里的图片有什么区别?

其实这个考的是他们加载的的2个方法

[UIImage imageNamed:@"xx"];  和[UIImage imageWithContentsOfFile:path];

1、前者比后者代码少,因为前者系统会去根据不同屏幕去加载不同的尺寸的图片@2x @3x,而后者要自己判断

2、前者可以缓存到内存可以多次使用,后者使用过后就销毁了不长期占用内存,这条才是重点

然而他们的缺点也就暴露了,所以看情况而定在项目中使用哪种吧

78、你们项目一般用什么布局?

这个时候你就要说出代码布局和xib或者storyboard的优缺点

1、代码好处就是好维护,修改起来方便

2、xib/storyboard 写界面快,不好就是修改约束麻烦,如果多人开发容易冲突

3、还有一点他们加载速度不一样,代码快,xib/storyboard慢,主要体现在打开xcode ,如果是运行的话其实看不出来,但是还是代码快

总结一句话:想后期维护便捷呢,就用代码,只想开发一个版本呢,就用xib

79、frame 和 bounds 的理解?

frame:当前视图在父视图的位置和大小

bounds:当前视图在自身坐标系统中的位置大小,默认x,y{0,0};

区别:frame决定自己在父控件的位置和大小,父控件位置要变

bounds决定子控件在自己内部的位置和大小,父控件位置不变

比如父控件的bounds,x-20,父控件不会有变化,但是子控件会像下移动20像素

比如:系统tableview的cell在屏幕上滑动就是利用这个原理bounds的y变化影响cell的上下滑动,而cell的frame的值没有任何变化

80、__block和__weak的作用及区别?

1、__block不管是ARC还是MRC模式下都可以使用,而__weak只能在ARC模式下使用。

2、__block可以修饰对象和基本数据类型。 __weak只能修饰对象。

3、__block对象可以在block中被重新赋值,__weak不可以被重新赋值。

4、当用__block和__weak分别修饰一个对象,__block会持有该对象,即使超出了该对象的作用域,该对象还是会存在的,直到block对象从堆上销毁;而__weak仅仅是将该对象赋值给weak对象,当该对象销毁时,weak对象将指向nil;

在block中防止循环引用需要注意的问题:

__block本身不能避免循环引用,如要避免要在block中把 __block修饰的对象置为nil。另外需要注意在MRC模式下__block是不会引起retain;但是在ARC模式下__block则会引起retain。所以ARC中建议使用__weak。

__weak可以避免循环引用,但是会导致外部对象释放后,block内部也访问不到该对象,可以通过在block内部声明一个__strong的变量,使其指向 weakObj,这样外部对象既可以在 block 内部保持住,又可以避免循环引用的问题。

81、NStimer准吗?谈谈你的看法?如果不准该怎样实现一个精确的NSTimer?

1.不准

2.不准的原因如下:

1、NSTimer加在main runloop中,模式是NSDefaultRunLoopMode,main负责所有主线程事件,例如UI界面的操作,复杂的运算,这样在同一个runloop中timer就会产生阻塞。

2、模式的改变。主线程的 RunLoop 里有两个预置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。

当你创建一个Timer 并加到 DefaultMode 时,Timer 会得到重复回调,但此时滑动一个ScrollView时,RunLoop 会将mode 切换为 TrackingRunLoopMode,这时 Timer

就不会被回调,并且也不会影响到滑动操作。所以就会影响到NSTimer不准的情况。

PS:DefaultMode 是 App 平时所处的状态,rackingRunLoopMode 是追踪 ScrollView 滑动时的状态。

方法一:

1、在主线程中进行NSTimer操作,但是将NSTimer实例加到main runloop的特定mode(模式)中。避免被复杂运算操作或者UI界面刷新所干扰。

self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(showTime) userInfo:nil repeats:YES];

[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];

2、在子线程中进行NSTimer的操作,再在主线程中修改UI界面显示操作结果;

- (void)timerMethod2 {

NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(newThread) object:nil];

[thread start];}

- (void)newThread

{@autoreleasepool

{[NSTimer scheduledTimerWithTimeInterval:1.0target:self selector:@selector(showTime) userInfo:nil repeats:YES];

[[NSRunLoop currentRunLoop] run];}}

总结:

一开始的时候系统就为我们将主线程的main runloop隐式的启动了。

在创建线程的时候,可以主动获取当前线程的runloop。每个子线程对应一个runloop

82、编写页面顶部轮播图,实现无限滚动(实现逻辑即可)?

现在的App开发中,轮播图几乎是一个不可避免的都会用到的。个人封装过轮播图,也看过很多种不同的轮播图,目前掌握的轮播图无限轮播有四种不同的实现方式:

第一种:基于collectionView进行的封装(推荐)

这种方式应该是实现起来最简单的一种方式了,也是个人最喜欢的一种封装方式。它的原理就是几个collectionView,至于无限轮播,很简单,只需要你的轮播数组给collectionView赋值的时候乘以一个较大的数字即可(例如100),collectionView本身处理了重用等一系列问题。

更好的实现代码:https://blog.csdn.net/Mo_Mo123/article/details/77824262

第二种:基于scrollView的无限轮播(首尾各多创建一个展示图片的ImageView)

这种实现方式个人感觉是最麻烦,而且还需要考虑重用等性能问题的一种。基本的原理就是在根据你轮播数组的个数在首尾各多创建一个ImageView,当然首位之前多创建一个展示轮播数组最后一个的ImageView,而尾部多创建一个展示轮播数组第一个的ImageView。

例如轮播数组有4张图。

3 0 1 2 3 0

当用户在滑到最左或者最右的瞬间将scrollView的偏移量进行改变,因为其首尾各有一张,用户在视觉上几乎感觉不出来。

这种的缺点就是如果轮播数组中图片太多,要考虑重用的问题。

第三种:同样是基于scrollView的无限轮播(总共就创建三个ImageView)

这种实现方式比第二种的好处就是不需要考虑重用问题,不论数组是多少个轮播图,我只创建三个ImageView。它与第二种的不同之处是其实用户每次看到的一直都是中间那张的ImageView,只是上边的内容改变了。其内部实现其实是在不断的改变那个轮播数组。

第四种:只有一个ImageView

这种实现方式不再基于ScrollView,同样不存在重用等的问题。这种实现方式跟第三种有相似之处,但是它跟第三种的区别是不再使用scrollView的图片切换方式。还是不停地去改变这个数组的内容。这种实现方式的核心在于切换的时候使用自定义的layer层的转场动画。模拟scrollView的滑动效果。

82、layoutSubview什么情况下会调用?

layoutSubviews作用

layoutSubviews是对subviews重新布局。比如,我们想更新子视图的位置的时候,可以通过调用layoutSubviews方法,即可以实现对子视图重新布局。

layoutSubviews默认是不做任何事情的,用到的时候,需要在子类进行重写。

注意:init初始化不会触发layoutSubviews。

1、addSubview会触发layoutSubviews。

2、设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化。

3、滚动一个UIScrollView会触发layoutSubviews。

4、旋转Screen会触发父UIView上的layoutSubviews事件。

5、改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件。

6、直接调用setLayoutSubviews。

7、直接调用setNeedsLayout。

83.__weak 和 _Unsafe_Unretain 的区别?

weak 修饰的指针变量,在指向的内存地址销毁后,会在 Runtime 的机制下,自动置为 nil。

_Unsafe_Unretain不会置为 nil,容易出现 悬垂指针,发生崩溃。但是 _Unsafe_Unretain 比 __weak 效率高。




84、atomic是百分之百线程安全的吗?



atomic不是百分之百线程安全的,只是保证多线程的情况下,getter、setter方法调用的完整性,在多线程访问的情况下,能够有效完整的调用完setter、getter方法。


但是在多线程并发的情况下,得到的结果不能够保证是统一的,比如说线程A在调用属性M的setter方法并且进行到了一半的时候,线程B调用了getter方法想要获取到M的内容,那么线程B拿到的是线程A赋值之前的内容,如果需求是要获取到线程A赋完值以后的内容,那么这就是线程不安全的实例。




83、ARC中的属性标示符


@property(assign/retain/strong/weak/unsafe_unretained/copy)NSObject *object;

assign 表示setter仅仅是一个简单的赋值操作,通常用于基本的数值类型,例如CGFloat和NSInteger等,修饰oc对象容易发生野指针


strong 属性定义了强引用关系,当这个属性设置一个新值的时候:首先对新值retain,对旧值release,然后再赋值。


weak 属性定义了弱引用关系,当给属性设置一个新值的时候,只是跟assign一样简单的赋值,如果当属性指向的对象销毁的时候,该属性会被置nil


unsafe_unretained 也是定义了一个弱引用关系,与weak类似,不同点是unsafe_unretained修饰的属性,指向的对象如果释放,本属性不会置nil


copy 类似于strong,不过在赋值时进行copy操作而不是retain操作,通常用于保留某个不可变的对象,防止它的意外改变时使用

1 loadView方法作用

创建控制器的view

第一次使用控制器View的时候调用,在控制器View的get方法中调用(使用的是懒加载)

当外界第一个使用当前控制器的View时,会调用当前一个方法loadView,创建控制器的View, 控制器的View是懒加载的,什么时候使用,什么时候才去创建,如果已经创建,就不会再创建了。

默认控制器View背景颜色是[UIColor clearColor]

2 loadView内部实现

首先会判断当前控制器是否从StoryBoard中加载,如果是,直接加载,如果不是,再判断有没有Xib来描述控制器的View,如果有,从Xib中加载,如果没有,loadView会直接通过[[UIView alloc] init]来创建。

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

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,036评论 1 32
  • 1.设计模式是什么? 你知道哪些设计模式,并简要叙述?设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类型...
    龍飝阅读 2,057评论 0 12
  • 1.设计模式是什么? 你知道哪些设计模式,并简要叙述? 设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类...
    司马DE晴空阅读 1,229评论 0 7
  • 把网上的一些结合自己面试时遇到的面试题总结了一下,以后有新的还会再加进来。 1. OC 的理解与特性 OC 作为一...
    AlaricMurray阅读 2,471评论 0 20
  • 会会念 周一的校会我们早就做好了充分的准备:往嘴巴里塞各种能充饥的,饼饼、面包,各种能抵饿的水果……总之,就怕那肚...
    陌上花gyx阅读 75评论 0 0