美文网首页
iOS 基础学习

iOS 基础学习

作者: 桀骜不驯的搬砖者 | 来源:发表于2019-03-18 12:30 被阅读0次
首先感谢这些博主:我这里只是学习和摘抄

七秒记忆的鱼儿 -- 2017年iOS面试题总结
就叫yang -- # iOS基础 # iOS面试题一
iOS-Interview -- 有条理性

iOS 系统层次架构

iOS 系统层次架构

1、属性关键字

#默认值:
  1.数据类型
    atomic assign readwrite
  2.对象类型
    atomic strong readwrite

1). readwrite 是可读可写特性;需要生成getter方法和setter方法时

2). readonly 是只读特性 只会生成getter方法 不会生成setter方法 ;不希望属性在类外改变

3). assign 是赋值特性,setter方法将传入参数赋值给实例变量;仅设置变量时;
    assign 主要用于修饰基本数据类型,如NSInteger和CGFloat,这些数值主要存在于栈上。

4). weak表示指向但不拥有该对象。其修饰的对象引用计数不会增加。无需手动设置,该对象会自行在内存中销毁。

5). copy 表示赋值特性,setter方法将传入对象复制一份;需要完全一份新的变量时。

6). nonatomic 非原子操作,决定编译器生成的setter getter是否是原子操作,atomic表示多线程安全,一般使用nonatomic

7). strong 表示指向并拥有该对象。其修饰的对象引用计数会增加1。该对象只要引用计数不为0则不会被销毁。

8). retain 表示持有特性,setter方法将传入参数先保留,再赋值,传入参数的retaincount会+1;
iOS9的几个新关键字: nonnull、nullable、null_resettable、__null_unspecified
1. nonnull:字面意思就能知道:不能为空(用来修饰属性,或者方法的参数,方法的返回值)
//三种使用方式都可以
 @property (nonatomic, copy, nonnull) NSString *name;
 @property (nonatomic, copy) NSString * _Nonnull name;
 @property (nonatomic, copy) NSString * __nonnull name;

 //不适用于assign属性,因为它是专门用来修饰指针的
@property (nonatomic, assign) NSUInteger age;

 //用下面宏包裹起来的属性全部都具nonnull特征,当然,如果其中某个属性你不希望有这个特征,也可以自己定义,比如加个nullable
//在NS_ASSUME_NONNULL_BEGIN和NS_ASSUME_NONNULL_END之间,定义的所有对象属性和方法默认都是nonnull

//也可以在定义方法的时候使用
//返回值和参数都不能为空
 - (nonnull NSString *)test:(nonnull NSString *)name;
 //同上
 - (NSString * _Nonnull)test1:(NSString * _Nonnull)name;

2. nullable 表示可以为空。
//三种使用方式
 @property (nonatomic, copy, nullable) NSString *name;
 @property (nonatomic, copy) NSString *_Nullable name;
 @property (nonatomic, copy) NSString *__nullable name;
3、null_resettable: get:不能返回空, set可以为空(注意:如果使用null_resettable,
必须重写get方法或者set方法,处理传递的值为空的情况)

 // 书写方式:
 @property (nonatomic, copy, null_resettable) NSString *name; 
 设置一下set或get方法
- (void)setName:(NSString *)name
 {
    if (name == nil) {
        name = @"Jone";
     }
    _name = name;
 }

 - (NSString *)name
 {
     if (_name == nil) {
         _name = @"Jone";
     }
     return _name;
 }

4、_Null_unspecified:不确定是否为空

使用方式只有这两种:
 @property (nonatomic, strong) NSString *_Null_unspecified name; 
 @property (nonatomic, strong) NSString *__null_unspecified name; 
1.1深拷贝和浅拷贝?
浅拷贝:浅拷贝并不拷贝对象本身,只是对指向对象的指针进行拷贝
深拷贝:直接拷贝对象到内存中一块区域,然后把新对象的指针指向这块内存(生成新对象)

#在非集合类对象中:
[immutableObject copy] // 浅复制
[immutableObject mutableCopy] //深复制
[mutableObject copy] //深复制
[mutableObject mutableCopy] //深复制

#在集合对象中:
对 immutable 对象进行 copy,是指针复制【浅拷贝】, mutableCopy 是内容复制【深拷贝】;对 mutable 对象进行 copy 和 mutableCopy 都是内容复制。
#但是:集合对象的内容复制仅限于对象本身,对象里面的每个元素仍然是指针复制。
[immutableObject copy] // 浅复制
[immutableObject mutableCopy] //单层深复制
[mutableObject copy] //单层深复制
[mutableObject mutableCopy] //单层深复制
#iOS 中集合对象的“深拷贝”只拷贝了一个壳,对于壳内的每个元素是浅拷贝。
1.2.0 如何让自己的类用copy修饰符?
  若想令自己所写的对象具有拷贝功能,则需实现 NSCopying 协议。如果自定义的对象分为可变版本与不可变版本,
  那么就要同时实现 NSCopying 与 NSMutableCopying 协议。

具体步骤:
  1)需声明该类遵从 NSCopying 协议
  2)实现 NSCopying 协议。该协议只有一个方法:
   - (id)copyWithZone:(NSZone *)zone;
1.2 写一个setter方法用于完成@property (nonatomic,retain)NSString *name,写一个setter方法用于完成@property(nonatomic,copy)NSString *name
- (void)setName:(NSString*)str
{
   [str retain];
   [name release];
   name = str;
}
- (void)setName:(NSString *)str
{
   id t = [str copy];
   [name release];
   name = t;
}
1.3 用@property声明的 NSString / NSArray / NSDictionary 经常使用 copy 关键字,为什么?如果改用strong关键字,可能造成什么问题?
用 @property 声明 NSString、NSArray、NSDictionary 经常使用 copy 关键字,
是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,
他们之间可能进行赋值操作(就是把可变的赋值给不可变的),为确保对象中的字符串值不会无意间变动,
应该在设置新属性值时拷贝一份。

1. 因为父类指针可以指向子类对象,使用 copy 的目的是为了让本对象的属性不受外界影响,
使用 copy 无论给我传入是一个可变对象还是不可对象,我本身持有的就是一个不可变的副本。
2. 如果我们使用是 strong ,那么这个属性就有可能指向一个可变对象,
如果这个可变对象在外部被修改了,那么会影响该属性。

#总结:使用copy的目的是,防止把可变类型的对象赋值给不可变类型的对象时,
#     可变类型对象的值发送变化会无意间篡改不可变类型对象原来的值。
1.4.请解释以下keywords的区别: assign vs weak, __block vs __weak
它们都是是一个弱引用。 
assign适用于基本数据类型,weak是适用于NSObject对象,
assign其实也可以用来修饰对象,那么我们为什么不用它呢?因为被assign修饰的对象在释放之后,
指针的地址还是存在的,也就是说指针并没有被置为nil。
如果在后续的内存分配中,刚好分到了这块地址,程序就会崩溃掉。 
#而weak修饰的对象在释放之后,指针地址会被置为nil。所以现在一般弱引用就是用weak。 

首先__block是用来修饰一个变量,这个变量就可以在block中被修改(参考block实现原理) 
__block:使用__block修饰的变量在block代码快中会被retain(ARC下,MRC下不会retain) 
__weak:使用__weak修饰的变量不会在block代码块中被retain 
同时,在ARC下,要避免block出现循环引用 __weak 
1.5 synthesize和dynamic分别有什么作用
@synthesize的作用:
    为Property指定生成要生成的成员变量名,并生成getter和setter方法。
    用途:对于只读属性,如果同时重新setter和getter方法,就需要使用synthesize来手动合成成员变量
@dynamic的作用:
    告诉编译器,不用为指定的Property生成getter和setter。使用方式:当我们在分类中使用Property为类扩展属性时,
    编译器默认不会为此property生成getter和setter,这时就需要用dynamic告诉编译器,自己合成了

2、说一下appdelegate的几个方法?从后台到前台调用了哪些方法?第一次启动调用了哪些方法?从前台到后台调用了哪些方法?iOS应用程序生命周期(前后台切换,应用的各种状态)详解

image

3.ViewController生命周期

按照执行顺序排列:
1. initWithCoder:通过nib文件初始化时触发。
2. awakeFromNib:nib文件被加载的时候,会发生一个awakeFromNib的消息到nib文件中的每个对象。     
//如果不是nib初始化 上面两个换成 initWithNibName:bundle:
3. loadView:开始加载视图控制器自带的view。
4. viewDidLoad:视图控制器的view被加载完成。  
5. viewWillAppear:视图控制器的view将要显示在window上。
6. updateViewConstraints:视图控制器的view开始更新AutoLayout约束。
7. viewWillLayoutSubviews:视图控制器的view将要更新内容视图的位置。
8. viewDidLayoutSubviews:视图控制器的view已经更新视图的位置。
9. viewDidAppear:视图控制器的view已经展示到window上。 
10.viewWillDisappear:视图控制器的view将要从window上消失。
11.viewDidDisappear:视图控制器的view已经从window上消失。

4.self 和 super

self 是类的隐藏参数,指向当前调用方法的这个类的实例。
super是一个Magic Keyword,它本质是一个编译器标示符,和self是指向的同一个消息接收者。
不同的是:super会告诉编译器,调用class这个方法时,要去父类的方法,而不是本类里的。

5.事件传递和响应者链条

#响应者链条
1.如果view的控制器存在,就传递给控制器;如果控制器不存在,则将其传递给它的父视图
2.在视图层次结构的最顶级视图,如果也不能处理收到的事件或消息,
则其将事件或消息传递给window对象进行处理
3.如果window对象也不处理,则其将事件或消息传递给UIApplication对象
4.如果UIApplication也不能处理该事件或消息,则将其丢弃

#点击屏幕时是如何互动的?
1.iOS系统检测到手指触摸(Touch)操作时会将其打包成一个UIEvent对象,
2.并放入当前活动Application的事件队列中,
3.单例的UIApplication会从事件队列中取出触摸事件并传递给单例的UIWindow来处理,
4.UIWindow对象首先会使用hitTest:withEvent:方法寻找此次Touch操作初始点所在的视图(View),
即需要将触摸事件传递给其处理的视图,这个过程称之为hit-test view。

UIWindow实例对象会首先在它的内容视图上调用hitTest:withEvent:,
此方法会在其视图层级结构中的每个视图上调用pointInside:withEvent:
(该方法用来判断点击事件发生的位置是否处于当前视图范围内,以确定用户是不是点击了当前视图),
如果pointInside:withEvent:返回YES,否则继续逐级调用,直到找到touch操作发生的位置,
这个视图也就是要找的hit-test view。

#hitTest:withEvent:方法的处理流程如下:
1、首先调用当前视图的pointInside:withEvent:方法判断触摸点是否在当前视图内;
2。若返回NO,则hitTest:withEvent:返回nil;
3.若返回YES,则向当前视图的所有子视图(subviews)发送hitTest:withEvent:消息,
所有子视图的遍历顺序是从最顶层视图一直到到最底层视图,
即从subviews数组的末尾向前遍历,直到有子视图返回非空对象或者全部子视图遍历完毕;
4.若第一次有子视图返回非空对象,则hitTest:withEvent:方法返回此对象,处理结束;
如所有子视图都返回非,则hitTest:withEvent:方法返回自身(self)。

#事件的传递和响应分两个链:
#传递链:由系统向离用户最近的view传递。
UIKit –> active app’s event queue –> window –> root view –>……–>lowest view
#响应链:由离用户最近的view向系统传递。
initial view –> super view –> …..–> view controller –> window –> Application

6.category 的作用?它为什么会覆盖掉原来的方法?

美团--深入理解Objective-C:Category

一、什么是分类:利用Objective-C的动态运行时分配机制,可以为现有的类添加新方法
我们添加的实例方法,会被动态的添加到类结构里面的methodList列表里面。

1).Category 的实现原理?

  * Category 实际上是 Category_t的结构体,在运行时,新添加的方法,都被以倒序插入到原有方法列表的最前面,所以不同的Category,添加了同一个方法,执行的实际上是最后一个。

  * Category 在刚刚编译完的时候,和原来的类是分开的,只有在程序运行起来后,通过 Runtime ,Category 和原来的类才会合并到一起。

二、主要作用:
1. 将类的实现分开到不同的文件中
2. 对私有方法的前向引用
3. 向对象添加非正式协议

三、它为什么会覆盖掉原来的方法?
category的方法被放到了新方法列表的前面,而原来类的方法被放到了新方法列表的后面,
这也就是我们平常所说的category的方法会“覆盖”掉原来类的同名方法,这是因为运行时在查找方法的时候是顺着方法列表的顺序查找的,
它只要一找到对应名字的方法,就会罢休^_^,殊不知后面可能还有一样名字的方法。
所以我们只要顺着方法列表找到最后一个对应名字的方法,就可以调用原来类的方法

四、类别和类扩展的区别。
答:category和extensions的不同在于 后者可以添加属性。另外后者添加的方法是必须要实现的。
6.1.category为什么不能添加属性?
category 它是在运行期决议的,因为在运行期,对象的内存布局已经确定,如果添加实例变量就会破坏类的内部布局,
这对编译型语言来说是灾难性的。
extension看起来很像一个匿名的category,但是extension和有名字的category几乎完全是两个东西。 
extension在编译期决议,它就是类的一部分,在编译期和头文件里的@interface以及实现文件里的
@implement一起形成一个完整的类,它伴随类的产生而产生,亦随之一起消亡。extension一般用来隐藏类的私有信息,
你必须有一个类的源码才能为一个类添加extension,所以你无法为系统的类比如NSString添加extension。
但是category则完全不一样,它是在运行期决议的。 
就category和extension的区别来看,我们可以推导出一个明显的事实,
extension可以添加实例变量,而category是无法添加实例变量的。
6.2. 那为什么 使用Runtime技术中的关联对象可以为类别添加属性。
1.其原因是:关联对象都由AssociationsManager管理,
AssociationsManager里面是由一个静态AssociationsHashMap来存储所有的关联对象的。
这相当于把所有对象的关联对象都存在一个全局map里面。
而map的的key是这个对象的指针地址(任意两个不同对象的指针地址一定是不同的),
而这个map的value又是另外一个AssociationsHashMap,里面保存了关联对象的kv对。
2.如合清理关联对象?
runtime的销毁对象函数objc_destructInstance里面会判断这个对象有没有关联对象,
如果有,会调用_object_remove_assocations做关联对象的清理工作。

Objective-C Associated Objects 的实现原理


7.Block --Block的本质

1) 什么是Block和它的本质是什么(block 是带自动变量的匿名函数)
   block本质上也是一个OC对象,它内部也有个isa指针
   block是封装了函数调用以及函数调用环境的OC对象
   block是封装函数及其上下文的OC对象
2) Block 的类型:
   __NSGlobalBlock:不使用外部变量的block是全局block
   __NSStackBlock: 使用外部变量并且未进行copy操作的block是栈block
   __NSMallocBlock:对栈block进行copy操作,就是堆block,而对全局block进行copy,仍是全局block
    Block访问外界变量
    MRC 环境下:访问外界变量的 Block 默认存储栈中。
    ARC 环境下:访问外界变量的 Block 默认存储在堆中(实际是放在栈区,然后ARC情况下自动又拷贝到堆区),自动释放。

3) 在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上的几种情况?
  1.block作为函数返回值时
  2.将block赋值给__strong指针时
  3.block作为Cocoa API中方法名含有usingBlock的方法参数时
  4.block作为GCD API的方法参数时

4) block 为什么用copy 修饰?
   block在创建的时候,它的内存是分配在栈上的,而不是在堆上。他本身的作于域是属于创建时候的作用域,一旦在创建时候的作用域外面调用block将导致程序崩溃。
   因为栈区的特点就是创建的对象随时可能被销毁,一旦被销毁后续再次调用空对象就可能会造成程序崩溃,在对block进行copy后,block存放在堆区.

Block原理、Block变量截获、Block的三种形式、__block


8.消息的传递方式

KVC&KVO的实现原理

#1.KVC也叫键值编码
  KVC是基于runtime机制实现的,运用 isa 指针,通过 key 间接对对象的属性进行操作
  程序优先调用setKey属性值方法-->_key -->_isKey-->如果上面列出的方法或者成员变量都不存在,  系统将会执行该对象的setValue:forUndefinedKey:方法,默认是抛出异常。

#2.KVO也叫键值监听
  当某个类的属性第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,
  在这个派生类中重写基类中任何被观察属性的setter 方法。
  并修改当前 isa 指针指向这个类,从而在给被监控属性赋值时执行的是派生类的setter方法
  苹果还重写了class 方法,返回原来的类

#深入 如何手动调用KVO?
  子类setter方法剖析:KVO的键值观察通知依赖于 
  NSObject 的两个方法:willChangeValueForKey:和 didChangevlueForKey:,
  在存取数值的前后分别调用2个方法: 被观察属性发生改变之前,willChangeValueForKey:被调用,
  通知系统该 keyPath?的属性值即将变更;
  当改变发生后, didChangeValueForKey: 被调用,
  通知系统该 keyPath 的属性值已经变更;
  之后,observeValueForKeyPath:ofObject:change:context: 也会被调用。
  且重写观察属性的setter方法这种继承方式的注入是在运行时而不是编译时实现的。

#通知和代理有什么区别
   通知是观察者模式,适合一对多的场景
   代理模式适合一对一的反向传值
   通知耦合度低,代理的耦合度高

#block和delegate的区别
    1.delegate运行成本低,block的运行成本高
    block出栈需要将使用的数据从栈内存拷贝到堆内存,当然对象的话就是加计数,
    使用完或者block置nil后才消除。delegate只是保存了一个对象指针,直接回调,没有额外消耗。
    就像C的函数指针,只多做了一个查表动作。
    2.delegate更适用于多个回调方法(3个以上),block则适用于1,2个回调时。

9.设计模式

1. 单例模式

单例模式:
   简单的来说,一个单例类,在整个程序中只有一个实例,并且提供一个类方法供全局调用,
   在编译时初始化这个类,然后一直保存在内存中,到程序(APP)退出时由系统自动释放这部分内存。
优点:
  (1)、在整个程序中只会实例化一次,所以在程序如果出了问题,可以快速的定位问题所在;
  (2)、由于在整个程序中只存在一个对象,节省了系统内存资源,提高了程序的运行效率;
缺点:
  (1)、不能被继承,不能有子类;
  (2)、不易被重写或扩展(可以使用分类);
  (3)、同时,由于单例对象只要程序在运行中就会一直占用系统内存,该对象在闲置时并不能销毁,在闲置时也消耗了系统内存资源;
Q: 如果单例的静态变量被置为nil了,是否内存会得到释放?
   A1: 静态变量修饰的指针保存在了全局区域,不会被释放。但是指针保存的首地址关联的对象是保存在堆区的,是会被释放的。
   A2:不会被释放的,如果想要释放的话 需要重写attempDelloc方法,在里面讲onceToke置为nil,instance置为nil

2.代理模式

代理模式:
   一种一对一的消息传递方式,由代理对象、委托者、协议三部分组成。
Q:代理用weak还是assign ?
   A:assign是指针赋值,不对引用计数操作,使用之后如果没有置为nil,可能就会产生野指针;而weak一旦不进行使用后,永远不会使用了,就不会产生野指针。

3.观察者模式

观察者模式:
   一种一对多的消息传递方式,当一个物体发生变化时,会通知所有观察这个物体的观察者让其做出反应


10. 内存管理

10.1 iOS内存分区情况
1. 栈区(Stack)
  由编译器自动分配释放,存放函数的参数,局部变量的值等
  栈是向低地址扩展的数据结构,是一块连续的内存区域

2.堆区(Heap)
  由程序员分配释放
  是向高地址扩展的数据结构,是不连续的内存区域

3.全局区
  全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,
  未初始化的全局变量和未初始化的静态变量在相邻的另一块区域
  程序结束后由系统释放

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

5.代码区
  存放函数体的二进制代码

注:
  在 iOS 中,堆区的内存是应用程序共享的,堆中的内存分配是系统负责的
  系统使用一个链表来维护所有已经分配的内存空间(系统仅仅记录,并不管理具体的内容)
  变量使用结束后,需要释放内存,OC 中是判断引用计数是否为 0,如果是就说明没有任何变量使用该空间,那么系统将其回收
  当一个 app 启动后,代码区、常量区、全局区大小就已经固定,因此指向这些区的指针不会产生崩溃性的错误。
  而堆区和栈区是时时刻刻变化的(堆的创建销毁,栈的弹入弹出),所以当使用一个指针指向这个区里面的内存时,
  一定要注意内存是否已经被释放,否则会产生程序崩溃(也即是野指针报错)

iOS内存管理(MRC、ARC)深入浅出

1) 什么是 ARC?
  ARC全称是 Automatic Reference Counting,是Objective-C的内存管理机制。
  简单地来说,就是代码中自动加入了retain/release,原先需要手动添加的
  用来处理内存管理的引用计数的代码,可以自动地由编译器完成了。

#ARC的使用是为了解决:对象retain和release匹配的问题。
  以前手动管理造成内存泄漏或者重复释放的问题将不复存在。

2) 什么是 MRC?
  以前需要手动的通过retain去为对象获取内存,
  并用release释放内存。所以以前的操作称为MRC (Manual Reference Counting)。

#内存管理的原则
  1.自己生成的对象,自己持有
     // 使用了alloc分配了内存,obj指向了对象,该对象本身引用计数为1,不需要retain 
    id obj = [[NSObject alloc] init]; 
    // 使用了new分配了内存,objc指向了对象,该对象本身引用计数为1,不需要retain 
    id obj = [NSObject new];

  2.也可持有非自己生成的对象
    // NSMutableArray通过类方法array产生了对象(并没有使用alloc、new、copy、mutableCopt来产生对象),因此该对象不属于obj自身产生的
    // 因此,需要使用retain方法让对象计数器+1,从而obj可以持有该对象(尽管该对象不是他产生的)
    id obj = [NSMutableArray array];
    [obj retain];

  3.不再需要自己持有对象时释放
    id obj = [NSMutableArray array];  
    [obj retain];
    // 当obj不在需要持有的对象,那么,obj应该发送release消息
    [obj release];
    // 释放了对象还进行释放,会导致奔溃
    [obj release];
    
  4.非自己持有的对象无法释放
    // 释放一个不属于自己的对象
    id obj = [NSMutableArray array]; 
    // obj没有进行retain操作而进行release操作,然后autoreleasePool也会对其进行一次release操作,导致奔溃。
    [obj release];

# 针对[NSMutableArray array]方法取得的对象存在,自己却不持有对象,底层大致实现:
   + (id)object {
       //自己持有对象
       id obj = [[NSObject alloc]init];
       [obj autorelease];
       //取得的对象存在,但自己不持有对象
       return obj;
     }

#注意这些原则里的一些关键词与方法的对应关系: 
 『生成』- alloc\new\copy\mutableCopy 
 『持有』- retain 
 『释放』- release 
 『废弃』- dealloc
10.2 AutoreleasePool底层实现原理

Objective-C Autorelease Pool 的实现原理

Autorelease Pool作用:缓存池,可以避免我们经常写relase的一种方式。其实就是延迟release,
将创建的对象,添加到最近的autoreleasePool中,
等到autoreleasePool作用域结束的时候,会将里面所有的对象的引用计数器-1.

#autorelease何时释放
  1.在没有使用@autoreleasepool的情况,autorelease对象是在当前的runloop迭代结束时释放。
    每个runloop中都会创建一个 autoreleasepool 并在runloop迭代结束进行释放。
  2.如果是手动创建autoreleasepool,自己创建Pool并释放:

11.UITableview的优化方法 YYKit 作者--iOS 保持界面流畅的技巧

1、避免cell的过多重新布局,差别太大的cell之间不要选择重用。
2、提前计算并缓存cell的高度,内容
3、尽量减少动态添加View的操作
4、减少所有对主线程有影响的无意义操作
5、cell中的图片加载用异步加载,缓存等
6、局部更新cell
7、减少不必要的渲染时间,比如少用透明色之类的

13.网络

1. post 和 get 的区别
1.GET在浏览器回退时是无害的,而POST会再次提交请求。
2.GET产生的URL地址可以被Bookmark,而POST不可以。
3.GET请求会被浏览器主动cache,而POST不会,除非手动设置。
4.GET请求只能进行url编码,而POST支持多种编码方式。
5.GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
6.GET请求在URL中传送的参数是有长度限制的,而POST么有。
对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
7.GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
8.GET参数通过URL传递,POST放在Request body中。
2. iOS即时通讯,从入门到“放弃”?

14.多线程

1.并行 和 并发 有什么区别?

多线程

15.Runtime

关于 Runtime 的一些问题解答

1.isa指针的理解,对象的isa指针指向哪里?isa指针有哪两种类型?
*   isa 等价于 is kind of
    1)实例对象的 isa 指向类对象
    2)类对象的 isa 指向元类对象
    3)元类对象的 isa 指向元类的基类

*   isa 有两种类型
    1)纯指针,指向内存地址
    2)NON_POINTER_ISA,除了内存地址,还存有一些其他信息
2.能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?
  不能向编译后得到的类中增加实例变量;
  能向运行时创建的类中添加实例变量;

  1.因为编译后的类已经注册在 runtime 中,类结构体中的 objc_ivar_list 实例变量的链表和   
    instance_size 实例变量的内存大小已经确定,同时runtime会调用 class_setvarlayout 或 
    class_setWeaklvarLayout 来处理strong weak 引用.所以不能向存在的类中添加实例变量。
  2.运行时创建的类是可以添加实例变量,调用class_addIvar函数. 但是的在调用 
    objc_allocateClassPair 之后,objc_registerClassPair 之前,原因同上.
3.runtime如何实现weak变量的自动置nil?
runtime 对注册的类, 会进行布局,
对于 weak 修饰的对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为 key,
当此对象的引用计数为0的时候会 dealloc,假如 weak 指向的对象内存地址是a,
那么就会以a为键, 在这个 weak 表中搜索,找到所有以a为键的 weak 对象,从而设置为 nil。
4.runtime如何通过selector找到对应的IMP地址?
每一个类对象中都一个方法列表,方法列表中记录着方法的名称,方法实现,以及参数类型,
其实selector本质就是方法名称,通过这个方法名称就可以在方法列表中找到对应的方法实现.
5.objc在向一个对象发送消息时,发生了什么?
1.根据对象的isa指针找到类对象id,
2.在查询类对象里面的methodLists方法函数列表,
3.如果没有找到,再沿着superClass,寻找父类,
4.再在父类methodLists方法列表里面查询,
5.最终找到SEL,根据id和SEL确认IMP(指针函数),在发送消息;
6.当最终查询不到的时候我们会报unrecognized selector错误
6.objc中向一个nil对象发送消息将会发生什么?(返回值是对象,是标量,结构体)
1. 如果一个方法返回值是一个对象,那么发送给nil的消息将返回0(nil)。
例如:Person * motherInlaw = [ aPerson spouse] mother]; 
如果spouse对象为nil,那么发送给nil的消息mother也将返回nil。
2. 如果方法返回值为指针类型,
其指针大小为小于或者等于sizeof(void*),float,double,long double 
或者long long的整型标量,
发送给nil的消息将返回0。
3. 如果方法返回值为结构体,发送给nil的消息将返回0。
结构体中各个字段的值将都是0。其他的结构体数据类型将不是用0填充的。
4. 如果方法的返回值不是上述提到的几种情况,那么发送给nil的消息的返回值将是未定义的。
7.什么时候会报unrecognized selector错误?iOS有哪些机制来避免走到这一步?
消息转发机制
答:
objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该
类中的方法列表以及其父类方法列表中寻找方法运行,如果,在最顶层的父类中依然找不到相应的方
法时,会进入消息转发阶段,如果消息三次转发流程仍未实现,则程序在运行时会挂掉并抛出异常
unrecognized selector sent to XXX 

#原因:
当发送消息的时候,我们会根据类里面的methodLists列表去查询我们要动用的SEL,
当查询不到的时候,我们会一直沿着父类查询,
当最终查询不到的时候我们会报unrecognized selector错误
#处理机制:会有三次机会处理
#第一次机会:
当系统查询不到方法的时候,会调用+(BOOL)resolveInstanceMethod:(SEL)sel
动态添加一个新方法并执行的机会。
#第二次机会:
或者我们可以再次使用-(id)forwardingTargetForSelector:(SEL)aSelector
这个方法是系统提供的一个将 SEL 转给其他对象的机会
#第三次机会:
这一步是Runtime最后一次给你挽救的机会。
1.首先它会发送-methodSignatureForSelector:消息获得函数的参数和返回值类型。
2.如果-methodSignatureForSelector:返回nil,
Runtime则会发出-doesNotRecognizeSelector:消息,程序这时也就挂掉了。
3.如果返回了一个函数签名,Runtime就会创建一个NSInvocation对象
并发送-forwardInvocation:消息给目标对象。

16.RunLoop -- YYKit 大神对RunLoop的理解sunnyxx 大神对RunLoop 的理解 -- 视频

1.runloop是来做什么的?runloop和线程有什么关系?主线程默认开启了runloop么?子线程呢?
RunLoop:RunLoop 实际上就是一个对象,这个对象管理了其需要处理的事件和消息。
一般来说:一个线程一次只能执行一个任务,执行完成后线程就会退出。RunLoop是让线程能随时处理事件但并不退出一种机制。

runloop和线程的关系:线程和 RunLoop 之间是一一对应的,其关系是保存在一个全局的 Dictionary 里
主线程默认是开启一个runloop。也就是这个runloop才能保证我们程序正常的运行。
子线程是默认没有开始runloop的
2.runloop的mode是用来做什么的?有几种mode?
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,没有实际作用。
注意iOS 对以上5中model进行了封装
NSDefaultRunLoopMode;
NSRunLoopCommonModes
3.为什么把NSTimer对象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主运行循环以后,滑动scrollview的时候NSTimer却不动了?
NSTimer 对象是在 `NSDefaultRunLoopMode`下面调用消息的,
但是当我们滑动scrollview的时候,`NSDefaultRunLoopMode`模式就自动切换到`UITrackingRunLoopMode`模式下面,
却不可以继续响应nstime发送的消息。所以如果想在滑动scrollview的情况下面还调用NSTimer的消息,
我们可以把nsrunloop的模式更改为`NSRunLoopCommonModes`
4.AFNetworking 中如何运用 Runloop?

AFURLConnectionOperation 这个类是基于 NSURLConnection 构建的,其希望能在后台线程接收
Delegate 回调。为此 AFNetworking 单独创建了一个线程,并在这个线程中启动了一个 RunLoop:

+ (void)networkRequestThreadEntryPoint:(id)__unused object {
    @autoreleasepool {
        [[NSThread currentThread] setName:@"AFNetworking"];
        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
        [runLoop run];
    }
}

+ (NSThread *)networkRequestThread {
    static NSThread *_networkRequestThread = nil;
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{
        _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
        [_networkRequestThread start];
    });
    return _networkRequestThread;
}

RunLoop 启动前内部必须要有至少一个 Timer/Observer/Source,所以 AFNetworking 在 [runLoop run] 之前先创建了一个新的 NSMachPort 添加进去了。通常情况下,调用者需要持有这个 NSMachPort (mach_port) 并在外部线程通过这个 port 发送消息到 loop 内;但此处添加 port 只是为了让 RunLoop 不至于退出,并没有用于实际的发送消息。

- (void)start {
    [self.lock lock];
    if ([self isCancelled]) {
        [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
    } else if ([self isReady]) {
        self.state = AFOperationExecutingState;
        [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
    }
    [self.lock unlock];
}

当需要这个后台线程执行任务时,AFNetworking 通过调用 [NSObject performSelector:onThread:..] 将这个任务扔到了后台线程的 RunLoop 中。


说一下你对架构的理解? 技术架构如何搭建?

对于iOS架构的认识过程
iOS 从0到1搭建高可用App框架


支付宝支付流程?

相关文章

网友评论

      本文标题:iOS 基础学习

      本文链接:https://www.haomeiwen.com/subject/bsnquqtx.html