- 经过筛选文档内容和下午3小时的学习,认为如下方案具有可行性,计划从Objective-C开始学习.学习计划以库名作为大纲.
从7.1日开始
重点学习 :
-
Objective-C(10个子目录)
---- 16天 --- 结束时间 : 7.17
(runtime非常核心.围绕runtime,系统方法加载顺序,编程中常见方法3个点,串联本章相关知识,最后2天时间梳理)
-
NSObject
-
系统自主调用的方法 (可重写,重写super调用方法实现父类调用):
initialize
load
dealloc
description -
常用方法 :
-
copy
与mutableCopy
可变对象发送此消息 : copy 与 mutableCopy 都可生成新对象. copy生成的是不可变对象, mutableCopy生成可变对象. 生成的只是一个新的容器,容器内容未开辟新的空间.
-
> - `isKindOfClass` 与`isSubclassOfClass`
用于判断当前类的所属,方便下一步编程使用
-
runtime :
(KVC与KVO!
是runtime的具体应用)
>- 是oc的底层,基本通过 C 和汇编写 ,使c语言扩展出面向对象的功能.编译器会将 OC 代码转换成运行时代码,在运行时确定数据结构和函数.
- 方法的调用本质 : 对象发送消息.
与runtime 匹配的消息机制 :
// performSelector最多携带2个参数
performSelector : withObject : afterDelay :
performSelector : withObject : afterDelay : inModes :
performSelectorOnMainThread : withObject : waitUntilDone :
performSelectorOnMainThread : withObject : waitUntilDone : modes :
performSelector : OnThread : withObject : waitUntilDone :
performSelector : OnThread : withObject : waitUntilDone : modes :
forwoardingTargetForSelector :
resolveClassMethod :
doesNotRecognizeSelector :
classFallbacksForKeyedArchiver :
#import <objc/message.h>
或者直接导入
#import <objc/runtime.h>
实际应用 :
1. 获取某各类中所有的方法
2. 获取某个类中所有的属性
3. method swizzling (黑魔法)
如果类别中对系统方法进行method swizzling(方法交换),使用category中的load方法。
文档中说明了initialize和load的区别:
load是只要类所在文件被引用就会被调用,而initialize是在类或者其子类的第一个方法被调用前调用。所以如果类没有被引用进项目,就不会有load调用;但即使类文件被引用进来,但是没有使用,那么 initialize也不会被调用.
需要清楚的几个runtime相关概念 :
- Method
代表类中某个方法的类型 - Ivar
是表示成员变量的类型。
3.IMP
一个函数指针,由编译器生成.当你发起一个 ObjC 消息之后,最终它会执行的那段代码,就是由这个函数指针指定.IMP 这个函数指针指向该方法的实现.
-
runLoop :
RunLoop相关联类(CoreFoundation)
-
Core Foundation中关于RunLoop的5个类
-
uCFRunLoopRef
-
uCFRunLoopModeRef
-- RunLoop 模式 -
uCFRunLoopSourceRef -- 各种事件(渲染、点击等等)
-
uCFRunLoopTimerRef -- 基本上就是NSTimer
-
uCFRunLoopObserverRef -- Observer
-
CFRunLoopRef
- 各线程间RunLoop相互独立,不受影响
CFRunLoopModeRef
-
一个 RunLoop 包含若干个 Mode,每个Mode又包含若干个Source/Timer/Observer
-
每次RunLoop启动时,必须指定一种Mode,这个Mode称为CurrentMode
-
如果要切换Mode,要先退出当前Loop,再重新选择一个Mode后进入
-
这样做保证了各Mode下的 Source/Timer/Observer 相互独立,不受影响
-
系统默认注册了5个Mode:
-
CFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
-
UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
-
UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用(系统内部调用)
-
GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到(系统内部调用)
-
CFRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode(它可以同时表示多个Mode-->default、tracking)
-
CFRunLoopSourceRef
-
事件源,分为:
-
以前的分法:
-
Port-Based Sources
-
Custom Input Sources
-
Cocoa Perform Selector Sources
-
-
现在的分法(基于栈):
-
Source0:非基于Port的
-
Source1:基于Port的
-
-
-
在这里简单说明RunLoop工作原理的一部分:
-
用户的点击、摇晃等等一系列事件由Source1接收,包装成Event对象,交给Source0处理
CFRunLoopTimerRef
// 调用了scheduledTimer返回的定时器,已经自动被添加到当前runLoop中,而且是NSDefaultRunLoopMode
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
// 修改模式
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
uCFRunLoopObserverRef
- 函数:
/**
* 顺序:
1、创建observer
2、添加观察者
3、释放observer
*/
// 创建observer
//CFAllocatorGetDefault 默认值
//YES 表示重复监听
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
NSLog(@"----RunLoop状态发生改变---%zd", activity);
});
// 添加观察者:监听RunLoop的状态
//CFRunLoopGetCurrent() 表示要监听的RunLoop -- 这里是当前RunLoop
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
// 释放Observer
CFRelease(observer);
- 可监控的状态:
kCFRunLoopEntry = (1UL << 0),// 即将进入Loop
kCFRunLoopBeforeTimers = (1UL << 1),//即将处理Timer
kCFRunLoopBeforeSources = (1UL << 2),//即将处理Source
kCFRunLoopBeforeWaiting = (1UL << 5),//刚进入休眠
kCFRunLoopAfterWaiting = (1UL << 6),//刚从休眠中唤醒
kCFRunLoopExit = (1UL << 7),//即将退出Loop
kCFRunLoopAllActivities //所有活动状态
RunLoop应用场景
- 开启一个常驻线程(让一个子线程不进入消亡状态,等待其它线程发来消息,处理其它事件)
- 在子线程开一一个定时器
- 在子线程中进行一些长期监控(沙河监控、连网监控等)
- 可以控制定时器在特定模式下执行
- 可以让某些事件(任务、行为)在特定模式下执行
- 可以添加Observer 监听RunLoop的状态。如监听点击事件的处理(在所有点击事件之前做一些事情)
-
相关知识点 :
- runLoop在即将进入loop时,会创建自动释放池,这个创建order(次序)优先级最高(是-2147483647),保证创建释放池发生在其它所有事件回调之前
- 在RunLoop睡眠之前会释放旧的自动释放池,并创建新的自动释放池(kCFRunLoopBeforeWaiting)
即将退出RunLoop时,会释放自动释放池,这个释放操作的order是排在所有操作的最后的,优先级最低(是-2147483647),保证创建释放池发生在其它所有事件回调之后
( kCFRunLoopBeforeWaiting)
建议
: 在子线程中启动RunLoop,用自动释放池进行包装注:order 的数值越小,优先级越高
-
苹果官方RunLoop介绍文档
-
常用关键字
-
static
- 作用1:添加了static关键字的全局变量,只能在当前类文件访问;反之则可以全局访问(使用extern关键字访问,访问同时可以修改其值)
- 作用2:添加了static关键字的局部变量,会延长生命周期,为其开辟的内存会一直持有,直到程序结束才销毁;反之作用域结束,其持有内存销毁,再次访问,内存重新分配
作用1例:
//定义一个变量(未增加关键字static)
int test = 1;
//因此,下面段引用代码,可以写在当前工程任意地方
extern int text;
text = 2;
作用2例:
int test = 1;
static int text = 1;
-
const
const修饰的全局变量会变为全局常量,且只读(即不可更改的常量) ,但仍可全局访问
例如:
/**const修饰谁,谁就不可变*/
const NSInteger age = 5;
/**
* oc语法下,外界访问的是name,所以const修饰name
*/
NSString *const name = @"Jim";
// 下面这句代码const修饰的是*name
// const NSString *name = @"Jim";
extern
引用关键字
引用全局变量:extern 类型名 变量名
引用全局常量:extern const 类型名 变量名
const + static 与 extern 可以组合使用,将const和static修饰的全局常量放在一个空的.m文件中,然后在头文件中使用extern关键字对这些全局常量进行引用,使用这些常量时,只需要导入头文件。建议
将头文件引用的.m文件写相同名称,方便管理.
(其它不常用知识稍后再做汇总)
-
UIKit (28个子目录)
---- 30天 --- 结束时间 : 8.16
近来接触最为频繁的知识点 :
-
父子控制器
同一个View在同一个父控件上添加n次,等于添加一次,因为这个View占有的内存一直是同一份
-
如果2个控制器的View是父子关系(不管是直接还是间接的父子关系),那么这两个控制器也应是父子关系
- 详解:一个控制器上添加了另一个控制器的View,当子控制器也需要监听一些事件(例如系统事件:屏幕旋转、屏幕摇动),就需要将子控制器与父控制器关联起来-->addChideViewController : ...。子控制器能够监听到事件的另一个前提:当前子控制器的View在父控制器上。为了方便管理,建议不进行跨级设置父控制器。也就是说,直接添加在上一级控制器上就好了
- 移除:当View从父控制器上移除,要执行:[self.childViewController[index] removeFromSuperController]; 将View对应子控制器也移除
- 导航跳转:(push/pop)当子控制器的父控制器不是导航控制器,而父控制器的上一层是导航控制器,正确设置控制器的父子关系,可以实现子控制器的导航跳转。
- 模态视图:(present/dismiss)与导航是一个道理
- didMoveToParentViewController:当在成为某一个控制器的子控制器之后会调用的方法,前提-->系统添加子控制器(例如设置导航控制器的根控制器),自定义添加需要手动调用(根据需求决定是否调用),或者自定义控制器从父控制器上移除时系统会自动调用
视图消失
-
条件:设置View尺寸时用到的屏幕较大且有父控件, 而View本身较小,导致在运行在较小的屏幕上时View"消失"(实际上是随父控件等比例缩小)
-
解决:view.autoresizingMask = UIViewAutoresizingNone;
(知识点琐碎,最后2天时间梳理)
-
了解内部机制 :
---- NotificationCenter ---- 5天 --- 结束时间 : 8.21
---- CFNetwork ---- 10天 --- 结束时间 : 8.30
-
不做重点,为以后开发做准备 :
- Security
- MapKit
- XCTest
网友评论