Objective-C基础

1、ViewDidLoad的触发时机(viewDidLoad)

结论:
每次访问UIViewController的view而且view为nil,loadView方法就会被调用。
系统是在loadView方法中创建好UIViewController的view的。
viewDidLoad是在loadView方法创建view完毕后被调用的。

题外:
loadView是干嘛用的?
* loadView是手写ViewController时,生成当前VC视图的惰性方法,如果要自定义View,可以直接复写这个方法。
* 自定义的方法无需调用super方法。

2、View方法的先后顺序

执行顺序:
视图创建:init->viewDidLoad->viewWillAppear->viewDidAppear
视图消失:viewWillDisappear-> viewDidDisappear

当视图A切换到视图B的流程:
1、B视图 viewDidLoad
2、A视图 viewWillDisappear
3、B视图 viewVillAppear
4、A视图 viewDidDisappear
5、B视图 viewDidAppear

3、property

* 一类是表示原子性,有atomic和nonatomic,默认是atomic
* 一类是表示引用计数的,有assign(unsafe_unretained),strong,weak,copy, 默认是assign
* 一类是表示读写权限的,默认是readwrite(可读可写),还有就是readonly
    
assign与weak:共同点是都不会增加retaincount
区别是
a)assign不仅能用于基础数据类型,也能用于OC对象,但weak只能用于OC对象;
b)weak是弱引用,在其所引用的对象被释放后,会将变量置为nil,不会有野指针的现象。
c)weak的赋值方式是复制引用,而assign是复制数据。
d)weak只能用于ARC;

怎么用 copy 关键字?
* NSString、NSArray、NSDictionary 等等经常使用copy关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary
* block 也经常使用 copy 关键字

weak实现原理:https://www.jianshu.com/p/48044cc54392

4、KVO(KVC)、Delegate、NSNotification

简介:
KVO(键值监听),即Key-Value Observing,它提供一种机制,当指定的对象的属性被修改后,对象就会接受到通知,前提是执行了setter方法、或者使用了 KVC赋值。
KVC(键值编码),即Key-Value Coding,一个非正式的Protocol,使用字符串(键)访问一个对象实例变量的机制。而不是通过调用Setter、Getter方法等 显式的存取方式去访问。

KVO的使用:
注册观察者:addObserver:forKeyPath:options:context:
实现观察者:observeValueForKeyPath:ofObject:change:context:
移除观察者:removeObserver:forKeyPath:(对象销毁,必须移除观察者)

KVO的原理:
当你观察一个对象(称该对象为「被观察对象」)时,一个新的类会动态被创建(NSKVONotifying_XX)。
这个类继承自「被观察对象」所对应类的,并重写该被观察属性的setter方法;针对setter方法的重写无非是在赋值语句前后加上相应的通知(或方法调用);
最后,把「被观察对象」的isa指针(isa指针告诉Runtime系统这个对象的类是什么)指向这个新创建的中间类,对象就神奇变成了新创建类的实例。
虽然被观察对象的isa指针被修改了,但是调用其class方法得到的类信息仍然是它之前所继承类的类信息,而不是这个新创建类的类信息。
友链:https://www.jianshu.com/p/e59bb8f59302

KVO相关crash及防护:
1、对不存在的属性进行kvo观测
2、observe忘记写监听回调方法 observeValueForKeyPath
3、add和remove次数不匹配
4、监听者和被监听者dealloc之前没有remove(其实也原因3,但是监听者和被监听者的生命周期不同)

方案一、
  可以让被观察对象持有一个KVO的delegate,所有和KVO相关的操作均通过delegate来进行管理,delegate通过建立一张map来维护KVO整个关系。
中间层delegate的代理工作:
(1)如果出现KVO重复添加观察者或者重复移除观察者(KVO注册观察者与移除观察者不匹配)的情况,delegate可以直接阻止这些非正常的操作。
(2)被观察者dealloc之前,可以通过delegate自动将与自己有关的KVO关系都注销掉,避免了KVO的被观察者dealloc时仍然注册着KVO导致的crash。

方案二、
  我们可以让观察者在注册的过程中,将注册信息一同记录下来,然后使用某种方法在对象dealloc时,在记录的信息里找到对应的观察者,注销观察。
  此方案在宿主释放过程中嵌入我们自己的对象,使得宿主释放时顺带将我们的对象一起释放掉,从而获取dealloc的时机点。采用构建一个释放通知对象,通过AssociatedObject方式连接到宿主对象,在宿主释放时进行回调,完成注销动作。

Delegate简介:
代理设计模式,是iOS中一种消息传递的方式,由协议、代理对象、委托者组成。只能一对一。
协议(protocol):用来指定代理可以做什么,必须做什么。
代理:根据指定协议,完成委托方需要实现的方法。
委托:根据指定协议,指定代理必须完成和可以完成方法。


NSNotification简介:
NSNotificationCenter 较之于 Delegate 可以实现更大的跨度的通信机制,可以为两个无引用关系的两个对象进行通信。NSNotification是iOS中一个调度消息通知的类,采用单例模式设计。

NSNotification使用:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(xxx:) name:@"abc" object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self];
//或者
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"abc" object:nil];
[[NSNotificationCenter defaultCenter] postNotificationName:@"abc" object:nil];

NSNotification相关crash及防护:([https://www.cnblogs.com/Xylophone/p/6394056.html])
在退出A页面的时候没有把自身的通知观察者A给注销,导致通知发过来的时候抛给了一个已经释放的对象A,但该对象仍然被通知中心引用,也就是僵尸对象,从而导致程序崩溃 。(也就是说,在一个页面dealloc的时候,一定要把这个页面在通知中心remove掉,否则这个页面很有可能成为僵尸对象)。
但有两种情况需要注意:
(1)单例里不用dealloc方法,应用会统一管理;
(2)类别里不要用dealloc方法removeObserver,在类别对应的原始类里的dealloc方法removeObserver,因为类别会调用原始类的dealloc方法。(如果在类别里新写dealloc方法,原类里的dealloc方法就不执行了)

方案一、
  利用method swizzling hook NSObject的dealloc函数,在对象真正dealloc之前先调用一下[[NSNotificationCenter defaultCenter] removeObserver:self]即可。在其添加observer的时候,对observer动态添加标记flag。
方案二、
  在宿主释放过程中嵌入我们自己的对象,使得宿主释放时顺带将我们的对象一起释放掉,从而获取dealloc的时机点。显然AssociatedObject是我们想要的方案。相比Method Swizzling方案,AssociatedObject方案的对工程的影响范围小,而且只有使用自动注销的对象才会产生代价。

5、异常捕获机制

Objective-C 异常机制 :
* 开发者将引发异常的代码放在 @try 代码块中, 程序出现异常 使用 @catch 代码块进行捕捉;
* @try代码块:存放可能出现异常的代码, @catch代码块:异常处理逻辑, @finally代码块:回收资源;
* 一个@try 代码块可以对应多个@catch代码块;@try @catch @finally 的花括号不可省略;

异常处理过程
* 生成异常对象:@try 中出现异常, 系统会生成一个异常对象, 该对象提交到系统中,系统就会抛出异常
* 运行环境接收到异常对象时, 如果存在能处理该异常对象的 @catch 代码块, 就将该异常对象交给 @catch 处理, 该过程就是捕获异常, 如果没有 @catch 代码块处理异常, 程序就会终止
* 运行环境接收到异常对象时, 会依次判断该异常对象类型是否是 @catch 代码块中异常或其子类实例, 如果匹配成功, 被匹配的 @catch 就会处理该异常, 都则就会跟下一个 @catch 代码块对比

try/catch能抓的异常:
NSRangeException(越界)
NSInvalidArgumentException(调用方法出错)
NSInternalInconsistencyException(比如把NSDictionary当做NSMutableDictionary来使用)
NSGenericException(遍历的时候,对集合进行了插入/删除操作)
参考:[https://www.jianshu.com/p/d32778ed9dd3]

结论:
Apple虽然同时提供了错误处理(NSError)和异常处理(exception)两种机制,但是Apple更加提倡开发者使用NSError来处理程序运行中可恢复的错误。而异常被推荐用来处理不可恢复的错误。
Exception容易造成内存管理问题(文档有描述即使是arc下,也不是安全的);
(try代码块中的对象不释放,可以用compileflags的-fobjc-arc-exceptions解决或是修改成.mm文件)
Exception使用block造成额外的开销,效率较低等等。

6、+load、+initialize的区别

* load方法是runtime在加载类后,在main函数之前,向每个类和分类发送的消息,顺序是父类-子类-分类. 通常在这个地方做method swizzlling或者使用Router方式做解耦的时候,通常也会在这里做URL注册;
* initialize方法是一个惰性方法,被调用的前提是,该类有方法被调用,如果该类一直没有被使用过,则这个方法一直不会被调用。
相同点是,两个方法都是线程安全的。

7、NSError设计成二级指针原因

为了方便回传赋值。
指针作为参数传递的时候,指针本身是值传递。
在函数内对参数指针赋值,是不能改变原参数指针指向的值的。
参考:[https://www.jianshu.com/p/6a1cfef75092]

8、动画和前后台

现象:App进入后台时,所有动画会在瞬间全部完成,但是闭包返回finish是false。
处理:
1、在applicationWillEnterForeground代理方法里面重新开始所有的动画。
2、设置animation.removedOnCompletion = NO; (属性解释:当为真时,一旦活动持续时间已过,动画将从呈现树中删除。默认值为YES。)

9、常用关键字 static、const、 extern、define

当static关键字修饰局部变量时,只会初始化一次且在程序中只有一份内存;
关键字static不可以改变局部变量的作用域,但可延长局部变量的生命周期(直到程序结束才销毁)。

当static关键字修饰全局变量时,作用域仅限于当前文件,外部类是不可以访问到该全局变量的(即使在外部使用extern关键字也无法访问)。
优点:不会影响到其他文件, 同理也不受其他文件影响/不会有命名冲突

const 表示常量, 用来修饰右边的基本变量或指针变量, 由变量转为常量, 其不可以被修改, 在编译阶段会执行检查, 其存储区域位于常量区, 常用于配合 static 或 extern 使用。

extern:使用其来声明供外部使用(.h&.m)/使用其来声明引用外部全局变量等。

想要访问全局变量可以使用extern关键字(全局变量定义不能有static修饰)。

define:字符替换, 在预编译期间处理, 使用时系统直接进行的文本替换。可用于修饰数据, 函数, 结构体, 方法等, 系统不会对其做类型检查。


Static和extern的区别:
(1)extern修饰的全局变量默认是有外部链接的,作用域是整个工程,在一个文件内定义的全局变量,在另一个文件中,通过extern全局变量的声明,就可以使用全局变量。
(2)static修饰的全局静态变量,作用域是声明此变量所在的文件。

const 修饰其后面内容:
const NSString * name = @"Jersey";
使 *name 指针地址不可变, 实际指向内容不受影响, 修改指针地址编译器报错。
NSString const * name = @"Jersey";
同上面写法一致
NSString * const name = @"Jersey";
使 *name 指针指向内容不可变, 指针地址不受影响, 修改内容则编译报错。

define 与 const 选择
宏定义是在预编译期间处理, 在使用时系统直接进行的方法替换, 静态变量等则是在编译期间进行的。
宏定义不会系统不会做编译检查, 所以类型错误也能通过编译, const 则会做编译检查。
能显式的声明数据类型, 并且不会出现自己定义的宏被其他人员更换,导致出现难以排查的 Bug。
不过宏不仅能对数据类型进行定义, 还能对函数, 结构体, 方法等进行定义相对比起常量来说作用会更多一些。
编译时刻:宏是预编译, const是编译阶段
编译检查:宏不做检查, 有错误不会提示, const会检查, 有错误会提示
宏的优点:高效,灵活,可用于替换各种 函数,方法,结构体,数据等;
宏的缺点:由于在预编译期间完成, 大量使用宏, 容易造成编译时间久
const优点:编译器通常不为普通 const 常量分配存储空间, 而是将它们保存在符号表中, 这使得它成为一个编译期间的常量, 没有了存储与读内存的操作, 使得它的效率也很高, 相当于宏更加高效, 并且容错率很低。
const缺点:const 能定义的内容非常有限, 只能用于定义常量
宏定义所定义的生命周期与所在的载体的生命周期有关
const修饰具有就近性, 即 const 后面的参数是不可变的, const修饰的参数具有只读性, 如果试图修改, 编译器就会报错
苹果官方不推荐我们使用宏, 推荐使用const常量

10、创建字符串常量的方式

在.h文件中extern声明一些全局的常量,在.m实现

.h
extern NSString * const url;

.m
NSString * const url = @"www.baidu.com"

11、category和extension的区别

区别 Category Extension
添加成员变量 不可以 可以
添加属性 不可以(但通过runtime可以) 可以
添加方法 可以 可以
语法格式 @interface NSObject (category)/@implementation NSObject(category)//注意:括号里有名字 @interface ClassName ()/@implementation ClassName()//注意:括号里无名字;
1、Extension通常定义在主类.m文件中,Extension中声明的方法直接在主类的.m文件中实现。
写在.h文件里,属性、方法公有;
写在.m文件里,属性、方法私有。
2、Extension只声明方法,不实现方法,会报警告:Method definition for '方法名' not found
3、为什么要用Category?
当你已经封装好一个完整的类库,随着需求的变化,你发现需要给类库中某个类的增加新方法,为了防止误调用引起不必要的麻烦,可以通过增加这个类的分类达到同样的效果,方便管理。
4、Extension是Category的一种特殊形式。
5、Category里声明属性,不会自动生成带下划线的成员变量以及getter/setter方法,需要手动添加get/setter方法,通过runtime关联对象绑定,访问不会报错。
Extension是编译时自动生成带下划线的getter/setter方法。
6、Extension中声明的方法没被实现,编译器会报警,但是Category中的方法没实现编译器是不会有任何警告的。这是因为Extension是在编译阶段被添加到类中,而Category是在运行时添加到类中。
7、Extension不能像Category那样拥有独立的实现部分(@implementation部分),也就是说,Extension所声明的方法必须依托对应类的实现部分来实现。

动态库和静态库

(iOS里的动态库和静态库)
静态库:链接时,静态库会被完整地复制到可执行文件中,被多次使用就有多份冗余拷贝(图1所示)

系统动态库:链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存(图2所示)

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