iOS基础知识

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从栈上复制到堆上。包括以下几个情况:

  1. Block作为返回值时,类似在非ARC的时候,对返回值Block执行[[returnedBlock copy] autorelease];
  2. 方法的参数中传递Block时
  3. Cocoa框架中方法名中还有useringBlock等时
  4. 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:

  1. kCFRunLoopDefaultMode: App的默认 Mode,通常主线程是在这个 Mode 下运行的。
  2. UITrackingRunLoopMode: 界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响。
  3. UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用。
  4. GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到。
  5. 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

  1. UIWindow是一种特殊的UIView,通常在一个app中至少会有一个UIWindow。
  2. iOS程序启动完毕后,创建的第一个视图控件就是UIWindow,接着创建控制器的View,最后将控制器的View添加到UIWindow上,于是控制器的View就显示在屏幕上了。
  3. 一个iOS程序之所以能显示在屏幕上,完全是因为它有UIWindow,也就是说,没有UIWindow就看不到任何UI界面。
  4. 状态栏和键盘都是特殊的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

  1. 在protocol中使用property只会生成setter和getter方法声明,我们使用属性的目的,是希望遵守我协议的对象的实现该属性
  2. 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常用功能

  1. Time Profiler:性能分析
  2. Zombies:检查是否访问了僵尸对象
  3. Allocations:用来检查内存,写算法的那批人也用这个来检查
  4. 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

你可能感兴趣的:(iOS基础知识)