1. load方法和initialize方法
相同点
- 在不考虑开发者主动使用的情况下,系统最多会调用一次
- 如果父类和子类都被调用,父类的调用一定在子类之前
- 都是为了应用运行提前创建合适的运行环境
- 在使用时都不要过重地依赖于这两个方法,除非真正必要
load
- 调用时间早于initialize,main函数之前,不会触发initialize的调用
- 对于有依赖关系的类,要确保被依赖类的load优先调用
- 没有load方法的实现,不会调用父类的的load方法
- Category的load也会收到调用,顺序在主类之后
- Method Swizzleu一般会放在load中执行
initialize
- 懒加载,第一次主动使用类时调用
- 子类不实现initialize方法,会把父类的实现继承过来调用一遍
- 都是线程安全的,不要再使用锁
2.block
数据结构
struct Block_descriptor {
unsigned long int reserved;
unsigned long int size;
void (*copy)(void *dst, void *src);
void (*dispose)(void *);
};
struct Block_layout {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor *descriptor;
/* Imported variables. */
};
类型
- _NSConcreteGlobalBlock 全局的静态 block,不会访问任何外部变量。
- _NSConcreteStackBlock 保存在栈中的 block,当函数返回时会被销毁。
- _NSConcreteMallocBlock 保存在堆中的 block,当引用计数为 0 时会被销毁。
变量的复制
- block外变量的引用,是复制到数据结构中访问
- __block修饰的外部变量,是复制其引用地址来实现访问
ARC和MRC下的block
配置在栈上的Block也就是NSStackBlock类型的Block,如果其所属的变量作用域结束该Block就会废弃。这个时候如果继续使用该Block,就应该使用copy方法,将NSStackBlock拷贝为NSMallocBlock。当NSMallocBlock的引用计数变为0,该NSMallocBlock就会被释放。
如果是非ARC环境,需要显式的执行copy或者antorelease方法。
而当ARC有效的时候,实际上大部分情况下编译器已经为我们做好了,自动的将Block从栈上复制到堆上。包括以下几个情况:
- Block作为返回值时,类似在非ARC的时候,对返回值Block执行[[returnedBlock copy] autorelease];
- 方法的参数中传递Block时
- Cocoa框架中方法名中还有useringBlock等时
- GCD相关的一系列API传递Block时
对于非ARC下,为了防止循环引用,我们使用__block
来修饰在Block中使用的对象。
对于ARC下,为了防止循环引用,我们使用__weak
来修饰在Block中使用的对象。
循环引用和野指针
block和对象互相强引用,引起引用循环,使用弱引用打破循环。block使用的对象被提前释放,产生了野指针,会引起crash,要注意对象的生命周期。
参考链接
谈Objective-C block的实现
正确使用Block避免Cycle Retain和Crash
3.ARC
ARC是编译特性,不是垃圾回收,本质还是计数式内存管理,在ARC特性下有4种与内存管理息息相关的变量所有权修饰符值得我们关注:
变量所有权修饰符 | 属性修饰符 |
---|---|
__strong | copy retain strong |
__weak | weak |
__autoreleasing | N/A |
__unsafe_unretaied | assign unsafe_unretained |
__autoreleasing
在 ARC 模式下,我们不能显示的使用 autorelease 方法了
__unsafe_unretained
ARC 是在 iOS5 引入的,而 __unsafe_unretained 这个修饰符主要是为了在 ARC 刚发布时兼容 iOS4 以及版本更低的系统,因为这些版本没有弱引用机制
关于 Toll-Free Bridging
在 MRC 时代,由于 Objective-C 类型的对象和 Core Foundation 类型的对象都是相同的 release 和 retain 操作规则,所以 Toll-Free Bridging 的使用比较简单,但是自从切换到 ARC 后,Objective-C 类型的对象内存管理规则改变了,而 Core Foundation 依然是之前的机制,换句话说,Core Foundation 不支持 ARC。是苹果在引入 ARC 之后对 Toll-Free Bridging 的操作也加入了对应的方法与修饰符,用来指明用哪种规则管理内存,或者说是内存管理权的归属。这些方法和修饰符分别是:
- __bridge(修饰符)-- 只是声明类型转变,但是不做内存管理规则的转变。
- __bridge_retained(修饰符) or CFBridgingRetain(函数)-- 内存管理的责任由原来的 Objective-C 交给Core Foundation 来处理,也就是,将 ARC 转变为 MRC
- __bridge_transfer(修饰符) or CFBridgingRelease(函数)-- 内存管理的责任由 Core Foundation 转交给 Objective-C,即将管理方式由 MRC 转变为 ARC。
参考链接:
iOS ARC 内存管理要点
4.RunLoop
概念
线程能随时处理事件但并不退出的模型通常被称为Event Loop,RunLoop实际上是一个对象,这个对象管理了其需要处理的事件和消息,并提供了一个入口函数来执行上面 Event Loop 的逻辑。线程执行了这个函数后,就会一直处于这个函数内部 "接受消息->等待->处理" 的循环中,直到这个循环结束(比如传入 quit 的消息),函数返回。
和线程的关系
RunLoop和线程一一对应,RunLoop的创建发生在线程第一次获取时,只能在线程内获取自身的RunLoop(主线程除外)。苹果不允许直接创建 RunLoop,它只提供了两个自动获取的函数:CFRunLoopGetMain() 和 CFRunLoopGetCurrent()。
相关类
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef
一个 RunLoop 包含若干个 Mode,每个 Mode 又包含若干个 Source/Timer/Observer。每次调用 RunLoop 的主函数时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode。如果需要切换 Mode,只能退出 Loop,再重新指定一个 Mode 进入。这样做主要是为了分隔开不同组的 Source/Timer/Observer,让其互不影响。
Mode
系统默认注册了5个Mode:
- kCFRunLoopDefaultMode: App的默认 Mode,通常主线程是在这个 Mode 下运行的。
- UITrackingRunLoopMode: 界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响。
- UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用。
- GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到。
- kCFRunLoopCommonModes: 这是一个占位的 Mode,没有实际作用。
参考链接
深入理解RunLoop
5.UIView
UIView继承于UIResponder类,因此它主要表达了两个意思
1.可视(CALayer)
2.可互交(UIResponder)
每个UIView都有一个隐式层(implicit Layer),View本身就是这个隐式层的Delegate.
Layer又有两部分组成。present layer和Model layer.
present layer表示了中间的过程状态,而Model layer则表示了起始和结束状态
loadView
loadView在View为nil时调用,早于ViewDidLoad,通常用于代码实现控件,收到内存警告时会再次调用。loadView默认做的事情是:加载nib。如果你想自己创建View对象,那么可以重载这个方法。
UIWindow
- UIWindow是一种特殊的UIView,通常在一个app中至少会有一个UIWindow。
- iOS程序启动完毕后,创建的第一个视图控件就是UIWindow,接着创建控制器的View,最后将控制器的View添加到UIWindow上,于是控制器的View就显示在屏幕上了。
- 一个iOS程序之所以能显示在屏幕上,完全是因为它有UIWindow,也就是说,没有UIWindow就看不到任何UI界面。
- 状态栏和键盘都是特殊的UIWindow。
UIWindow有三个层级,分别是Normal,StatusBar,Alert。
keyWindow是指定的用来接收键盘以及非触摸类的消息,而且程序中每一个时刻只能有一个window是keyWindow。
6.沙盒和bundle
沙盒目录结构
- Application:存放程序源文件,上架前经过数字签名,上架后不可修改
- Documents:常用目录,iCloud备份目录,存放数据,这里不能存缓存文件,否则上架不被通过
Library - Caches:存放体积大又不需要备份的数据,SDWebImage缓存路径就是这个
Preference:设置目录,iCloud会备份设置信息 - tmp:存放临时文件,不会被备份,而且这个文件下的数据有可能随时被清除的可能
App Bundle 里面有什么
- Info.plist:此文件包含了应用程序的配置信息.系统依赖此文件以获取应用程序的相关信息
- 可执行文件:此文件包含应用程序的入口和通过静态连接到应用程序target的代码
- 资源文件:图片,声音文件一类的
- 其他:可以嵌入定制的数据资源
7.属性
修饰符
- atomic,nonatomic -- 原子性
- readonly,readwrite -- 访问权限
- assign,strong,weak,copy -- ARC内存管理
- assign,retain,copy -- MRC内存管理
- setter=,getter= -- 指定方法名称
assign和weak的区别
assign修饰的数据内存由系统清理,对于基础数据类型,生存周期为整个栈周期,可以由栈处理。修饰对象时,如果这个对象被其他过程释放(即指向的堆内存被释放),对象的指针地址依然存在,形成野指针。weak不能修饰基础类型数据,修饰对象时,如果对象被释放,指针会自动置为nil。
实现原理
在普通的OC对象中,@property就是编译器自动帮生成一个私有的成员变量和setter与getter方法的声明和实现
protocol 和 category
- 在protocol中使用property只会生成setter和getter方法声明,我们使用属性的目的,是希望遵守我协议的对象的实现该属性
- category 使用 @property 也是只会生成setter和getter方法的声明,如果我们真的需要给category增加属性的实现,需要借助于运行时的两个函数
objc_setAssociatedObject
objc_getAssociatedObject
8.cocoapods原理
- 将所有的依赖库都放到另一个名为 Pods 项目中
- Pods 项目最终会编译成一个名为 libPods.a 的文件,主项目只需要依赖这个 .a 文件即可。这样,依赖库源码管理工作都从主项目移到了 Pods 项目中。
- 对于资源文件,CocoaPods 提供了一个名为 Pods-resources.sh 的 bash 脚本,该脚本在每次项目编译的时候都会执行,将第三方库的各种资源文件复制到目标目录中。
- CocoaPods 通过一个名为 Pods.xcconfig 的文件来在编译时设置所有的依赖和参数。
9. Instruments常用功能
- Time Profiler:性能分析
- Zombies:检查是否访问了僵尸对象
- Allocations:用来检查内存,写算法的那批人也用这个来检查
- Leaks:检查内存,看是否有内存泄露
10.nil / Nil / NULL / NSNull
标志 | 值 | 含义 |
---|---|---|
NULL | (void *)0 | C指针的字面零值 |
nil | (id)0 | Objective-C对象的字面零值 |
Nil | (Class)0 | Objective-C类的字面零值 |
NSNull | [NSNull null] | 用来表示零值的单独的对象 |
to be continued