目录
知识点
1.类别和扩展
2.@修饰符
3.const , #define , typedef,extern,static
4.野指针和空指针的区别
5.self.name和_name的区别
6.id与instancetype与void *的区别
7.nil、Nil、Null、NSNull的区别
8.#include 和 #import 的区别
9.循环引用
单例
多线程
block
KVC
KVO
内存管理机制
Category
架构模式
0. 知识点
- 类别和扩展
类别 Category
只能添加方法,不能添加属性(因为添加属性时,不会自动生成set、get方法。可使用runtime动态运行时机制来添加,但只有在运行时才存在)
级别高于普通类,会覆盖普通类中同名的方法(因为先编译普通类,再编译类别)。若两个类别都实现了,则决定于头文件的添加顺序(排在下边的优先级高)。
扩展
只存在于.m文件中.
@interface Person(){
NSString *name;
}
@property (nonatomic,readwrite) NSArray *contentArray;
-(void)run;
@end
- @修饰符
@property修饰符
例:
@property (nonatomic,strong) NSArray *contentArray;
“访线指引”
访问权限相关 :readwrite、readonly
线程安全相关 :nonatomic、atomic(注意不写时:默认为atomic。原子性:一个操作要么执行要么不执行且执行中不会被打断,使用自旋锁不允许多线程同一时间访问,耗资源降低性能,且不一定线程安全。只会对set上锁,get没必要)
指定方法名 :setter=、getter=
引用计数相关 :copy、assign、weak、strong、retain
默认:(readwrite,assign, atomic)
copy
NSString/NSDictionary/NSArray使用copy,而不是strong(将NSMutableArray对象赋值给NSArray后,修改前者,后者内容也变了。) block使用copy,将block的值从栈区复制到堆区,改变block的作用域
assign :直接赋值(修饰基本数据类型、结构体、枚举---非类类型)
weak :弱引用,引用计数不+1 (仅修饰类类型,防止循环引用)(dele避免循环引用造成内存泄漏;控件---VC强引用self.view强引用subViews数组强引用控件,所以可以使用weak修饰)
strong :强引用,引用计数+1(仅修饰类类型,除了NSArray/NSDictionary/NSArray/.../block外的类类型)
深拷贝/浅拷贝
深拷贝:拷贝对象的值
浅拷贝:拷贝对象的指针
copy出来的对象:不可变。 (copy不可变类型:浅拷贝,copy可变类型:深拷贝)
mutableCopy出来的对象:可变。 (永远都是深拷贝)
错误观点:copy是浅拷贝,mutableCopy是深拷贝。 当用copy可变数组时
不完全拷贝/ 完全拷贝
不完全拷贝(默认,拷贝的内容中元素是类类型,则只拷贝该元素的指针)
完全拷贝(需要覆写copyWithZone:手动返回新实例)
修饰属性和方法的使用范围
@public : 所有地方都能使用
@private : 仅本类可用
@protected : 本类及子类
@package : 本应用程序包
修饰协议中方法
@optional : 协议可选实现方法
@required : 协议必须实现方法
修饰属性
@dynamic 属性;
其getter和setter方法编译器是不会自动生成,需手动实现,否则使用时崩溃.
@synthesize 属性;(默认情况下编译器自动生成)
其getter和setter方法编译器是会自动生成,不需手动实现。
修饰类
@class 类名;
作用:用于在.h中提前声明一个类,提高编译效率;.m依旧要引入该类#import""
- const , #define , typedef,extern,static
const 用来定义一个常量(存放在常量表中,编译时检查数据类型)
const int a=10; int const a=10; const与数据类型互换无影响
const Person *p; *p不可变 p可变 Person *const p; p不可变 *p可变
#define
宏定义(预编译指令,预处理阶段直接替换,不存在于常量表中),将前者替换成后者,编译时不检查数据类型
#define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
typdedef
给已有类型起别名(1.便捷:枚举,结构体,block;2.安全:给系统类型重命名),检查数据类型
typedef int MyIn; // typedef 旧 新;
extern
修饰全局变量且没有用static修饰时,其作用域为整个项目文件
// 定义
int gAge =10;
// 其他文件中获取
extern int gAge;
NSLog(@"%d", gAge);
static
修饰局部变量时:程序运行期间只会初始化一次 并且 直到整个程序结束时才被销毁
static int age = 20;
修饰全局变量时:作用域仅限于本类
比较
const和#define都可以定义常量,区别:
处理时间不同:#define预编译阶段(编译之前),const编译阶段
#define不能进行类型检测,const可以
#define可以定义函数,const只能定义常量
大量#define会造成编译时间过长
typdedef与#define的区别
typedef会对语法进行检测,#define直接替换
使用
应用的所有地址
方法1:新建.h文件(需要在pch中导入)
在.h中
// 开发中的API_URL使用(避免此文件外被改动)
static NSString * const loginURL = @"/login";
// 以下在这里错误,loginURL依然可以被改成其他值
static NSString const * loginURL = @"/login";
方法2:新建.h.m文件(需要在pch中导入)
在.h中
extern NSString * const loginURL;
在.m中
NSString * const loginURL = @"name";
- 野指针和空指针的区别
野指针和空指针的区别
野指针:
指针指向了一个被释放的对象(僵尸对象,造成的原因:过多地release对象)(所指向的地址有个“标识”:标识此为僵尸对象)
只要给野指针发送消息,程序就会报错(为了避免,对象释放后,设置为nil)。
空指针:
指针指向nil(指针设置为nil)。
给空指针发送消息不会报错。[nil Method]; // 不会崩
- self.name和_name的区别
self.name和_name的区别:
self.name会调用setget方法修改变量,而_name会直接修改变量。
self.name=nil和[self.name release]的区别:
self.name是通过set方法将name属性置为nil,[name release] 只是将name的引用计数减1,此时若name的引用计数不为0,还可以访问。
- id与instancetype与void *的区别
id与instancetype与void *的区别:
id可以指向任何类型的对象(不需要*)
instanceType只能作为返回值返回和该方法所在类相同类型的对象
void*指无类型指针(等同于id),指向任意类型的对象
- nil、Nil、Null、NSNull的区别
nil、Nil、Null、NSNull的区别:
nil :表示空对象,调用nil不会崩溃
Nil :表示空类
Null :表示空指针,不指向任何内存地址
NSNULL :表示一个空的集合对象
8.#include 和 #import 的区别
#include 不能(会多次导入)
#import 不会重复导入头文件
#import<> 包含系统头文件(从系统库目录中查找)
#import"" 包含自定义头文件(查找自定义头文件)
- 循环引用
block
当block作为属性(copy),且block内部引用self,造成循环引用。
解决:
__weak BViewController *weakSelf = self;
self.block = ^{
NSLog(@"%@",weakSelf.name);
};
NSTimer
当timer作为属性,且设置self为target,造成循环引用
解决:使用weak修饰
delegate
当B是A的属性,且B的delegate属性是A,造成循环引用
解决:
delegate属性用weak修饰
NSObject
+(void)initialize{[super initialize];}
第一次使用该类时调用(会在init方法前调用)(类别优先级高)
+(void)load{}
应用启动后会 加载所有类的load方法(最先,会在main之前)(类别优先级高)
延迟加载:
重写get方法,用到时才加载
避免瞬时内存过高
异步加载:
避免线程堵塞
delegate
// dele使用weak:为了避免循环引用
@property (nonatomic,weak) id delegate;
例:
UITableView的delegate和dataSource都是代理
delegate提供交互
dataSource提供数据源
代理和block的区别:
相同点:都能传递值;都应避免循环引用;都应在使用前判断是否实现;
不同点:代理需要创建协议,用weak修饰dele属性,实现dele方法。block用copy修饰
View
// 构造(初始化)
-(instancetype)init{}
-(instancetype)initWithFrame:(CGRect)frame;
-(void)delloc{}; // 销毁
指针
int a[10]; // 10个int 元素
int *a[10]; // 10个指向int的指针 元素
int (*a)[10]; // 指向10个int元素数组 指针
int (*a[10])(int); // 10个指向函数(返回int参数int)的指针 元素
1. 单例
单例
程序运行期间(从点击App开始运行到关掉App结束运行),该类只会创建一个实例(对象指针存在于静态区,对象在堆中所占的空间 只会在程序终止后才会被释放)。
使用
YTAccount.h
#import <Foundation/Foundation.h>
@interface YTAccount : NSObject<NSCopying,NSMutableCopying>
+(instancetype)sharedAccount;
@end
YTAccount.m
#import "YTAccount.h"
@implementation YTAccount
static YTAccount *_sharedAccount;
// 用于外部访问
+(instancetype)sharedAccount{
return [self new];
}
// 调用alloc会调用本方法(若不实现,用allocWithZone多次创建对象得到的不是单例)
+(instancetype)allocWithZone:(struct _NSZone *)zone{
/*
// 方法一
// 线程锁(防止多线程同时调用)
@synchronized (self) {
if(!_sharedAccount){
_sharedAccount=[super allocWithZone:zone];
}
}
return _sharedAccount;
*/
// 方法二
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//
if(!_sharedAccount){
_sharedAccount=[super allocWithZone:zone];
}
});
return _sharedAccount;
}
// 避免使用copy mutableCopy方法时再次创建
-(id)copyWithZone:(NSZone *)zone{
return _sharedAccount;
}
-(id)mutableCopyWithZone:(NSZone *)zone{
return _sharedAccount;
}
@end
优化使用(方案一)
放在pch中
#define shareH(name) +(instancetype)share##name;
#if __has_feature(objc_arc)
#define shareM(name) static id _instance;\
+(instancetype)allocWithZone:(struct _NSZone *)zone\
{\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
_instance = [super allocWithZone:zone];\
});\
return _instance;\
}\
\
+(instancetype)share##name\
{\
return [[self alloc]init];\
}\
-(id)copyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
\
-(id)mutableCopyWithZone:(NSZone *)zone\
{\
return _instance;\
}
#else
#define singleM(name) static id _instance;\
+(instancetype)allocWithZone:(struct _NSZone *)zone\
{\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
_instance = [super allocWithZone:zone];\
});\
return _instance;\
}\
\
+(instancetype)share##name\
{\
return [[self alloc]init];\
}\
-(id)copyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
-(id)mutableCopyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
-(oneway void)release\
{\
}\
\
-(instancetype)retain\
{\
return _instance;\
}\
\
-(NSUInteger)retainCount\
{\
return MAXFLOAT;\
}
#endif
使用
.h中 shareH(类名)
.m中 shareM(类名)
优化使用(方案二)
实现一个单例基类
需要单例时继承即可
2. 多线程
2.1 概念
程序、进程、线程、多线程
启动一个应用程序后,会至少创建一个进程,该进程会创建一个主线程。
进程
1、简介
(1)一个进程是指在系统中正在运行的一个应用程序。
(2)进程间相互独立,每个进程均运行在其专用且受保护的内容空间内。
(3)通过“活动监视器”可以查看mac系统中所开启的进程。
2、进程通信
单机系统中进程通信有四种形式:主从式、会话式、消息或邮箱机制、共享存储区方式。
主从式例子:终端控制进程和终端进程。
会话式例子:用户进程与磁盘管理进程之间的通信。
线程
简介
(1)线程(Thread),也称作轻量级进程。线程是进程的组成部分,一个进程可以拥有多个线程(至少有一个主线程)。
(2)线程在程序中是独立的、并发的执行流,当进程被初始化后,主线程就被创建了。
(3)可以在进程内创建多条顺序执行流,这些顺序执行流就是线程,每条线程是相互独立的。
(4)线程是独立运行的,单个线程他是不知道进程中是否存在其他的线程,线程的执行是抢占式的。(当前运行的线程在任何时候都可能被挂起,以便另外一个线程可以运行)。
(5)3个基本状态:就绪,执行,阻塞
(6)一个进程要想执行任务,必须得有线程(每一个进程至少要有一条线程,可以有多个线程)。
(7)线程是进程的基本执行的单元,一个进程(程序)的所有的任务都在线程中执行。
(8)一个线程中的任务的执行是串行的。(如果要在一个线程中执行多个任务,那么只能一个一个地按照顺序执行这些任务)。
(9)在同一时间内,一个线程只能执行一个任务。
多线程
1、简介
(1)一个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务。
(2)多线程技术可以提高程序的执行效率。
2、原理
(1)同一时间,CPU只能处理一条线程,只有一条线程在工作(执行)。
(2)多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换)。
(3)如果CPU调度线程的时间足够快,就会造成了多个线程并发执行的假象。
(4)如果线程非常多,CPU会在N多线程之间调度,CPU会累死,消耗大量的CPU资源,每条线程被调用执行的频次会降低(线程的执行效率降低)。
3、优缺点
优点:
适当提高程序的执行效率、资源利用率(CPU、内存利用率)。
(1)对于单线程的应用而言,整个应用只有一个顺序执行流,当执行流在某个耗时操作或不能立即完成的任务时(如网络请求、复杂的操作和运行过程、某些必须消耗一定时间后才能完成的操作),该执行流就会被阻塞,整个应用就会被卡在那里无法继续执行,因此单线程的程序往往功能非常有限。
(2)在实际应用中多线程是非常有用的,例如:用户操作界面上的某个按钮时,该按钮需要执行的任务需要几秒甚至十几秒,如果直接在UI线程(主线程)中完成这个任务,在该任务执行完成、返回之间,UI线程被该任务阻塞,无法响应用户的其他操作(这段时间内,用户对iOS系统的操作将不会有任何反应,除非用户单机Home键返回,但这并不是用户希望的结果)。
缺点:
(1)开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能。
(2)线程越多,CPU在调度线程上的开销就越大。
(3)程序设计更加复杂:如线程之间的通信,多线程的数据共享。
4、在iOS开发中的应用
一个iOS程序运行后,默认会开启1条线程,称为主线程(UI线程)。
主线程用来
1、显示、刷新UI界面
2、处理UI事件(点击事件、滚动事件、拖曳事件等)。
3、比较耗时的操作不要放在主线程中。耗时操作会卡住主线程,严重影响UI的流畅度,给用户一种卡的坏的体验。
多线程资源共享的安全隐患
一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源。当多个线程访问同一资源时,很容易引发数据散乱和数据安全问题。(如多个线程访问同一个对象、同一个变量、同一个文件)。
解决方法:使用互斥锁
使用的格式:@synchronized(锁对象){//需要锁定的代码} 注:锁定一份代码只用一把锁,用多吧锁是无效的。
互斥锁的优点
能有效防止因多线程抢夺资源造成的数据安全问题
互斥锁的缺点
需要消耗大量的CPU资源。
互斥锁的使用前提
多条线程抢夺同一块资源。(互斥锁就是使用了线程同步技术,多条线程按顺序地执行任务)。
示例:在创建单例模式时,最好使用互斥锁。
static UserInfo *singletonUserInfo = nil;
+(UserInfo *)shareUserInfo{
@synchronized(self){
//创建一个线程锁,防止多线程中多次创建
if (singletonUserInfo == nil) {
singletonUserInfo = [[UserInfo alloc]init];
NSLog(@"初始化");
}
}
return singletonUserInfo;
}
atomic
原子性,为setter方法加锁(默认就是atomic),线程安全,需要消耗大量的资源
nonatomic
非原子性,不会为setter方法加锁,非线程安全,适合内存小的移动设备
iOS开发:最好所有属性都声明为nonatomic,尽量避免多线程抢夺同一块资源,尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力。
简单总结
1.一个程序至少有一个进程,一个进程至少有一个线程。
进程创建则主线程创建。
主线程死掉则进程死掉。
2.进程是最小的资源分配单位,线程是最小的CPU执行单位。
进程之间相互独立,且所有任务都交由线程去执行。
多个线程之间相互独立,但共享同一进程的资源。
3.更新UI要放在主线程中(否则:防止造成卡顿,避免多个子线程同时操作UI)
同步/异步/串行/并行
同步:任务块完成后再继续向下执行
异步:跳过任务块继续向下执行
串行:是指一个队列,队列中的任务依次执行
并行:是指一个队列,队列中的任务同时执行
4种(首先先考虑队列,再考虑同异步)
同步串行(死锁)
同步并行
异步串行
异步并行
2.2 使用
NSThread
创建线程
(方式一)(创建后调用start启动)
NSThread *thread=[[NSThread alloc]initWithTarget:self selector:@selector(method:) object:@""];
(方式二)(创建后调用start启动)
NSThread *thread1=[[NSThread alloc]initWithBlock:^{
}];
(方式三)(创建后自动启动)
[NSThread detachNewThreadSelector:@selector(method:) toTarget:self withObject:@""];
(方式四)(创建后自动启动)
[NSThread detachNewThreadWithBlock:^{
}];
(方式五)(隐式创建)
//
[self performSelector:@selector(method)];
[self performSelector:@selector(method) withObject:nil];
[self performSelector:@selector(method) withObject:nil withObject:nil];
[self performSelector:@selector(method) withObject:nil afterDelay:1.0];
[self performSelector:@selector(method) withObject:nil afterDelay:1.0 inModes:@[NSRunLoopCommonModes]];
//
[self performSelector:@selector(method) onThread:thread withObject:nil waitUntilDone:true];
[self performSelector:@selector(method) onThread:thread withObject:nil waitUntilDone:true modes:@[NSRunLoopCommonModes]];
//
[self performSelectorOnMainThread:@selector(method) withObject:nil waitUntilDone:true];
[self performSelectorOnMainThread:@selector(method) withObject:nil waitUntilDone:true modes:@[NSRunLoopCommonModes]];
//
[self performSelectorInBackground:@selector(method) withObject:nil];
thread实例方法
// 是否是主线程
BOOL isMain=[thread isMainThread];
// 是否被取消
BOOL isCanceled=[thread isCancelled];
// 是否正在执行
BOOL isExecuting=[thread isExecuting];
// 是否已结束
BOOL isFinished=[thread isFinished];
// 是否是主线程
BOOL isMainThread=[thread isMainThread];
// 获取线程名
NSString *threadName=thread.name;
// 设置线程名
[thread setName:@"name"];
// start
[thread start];
// cancel
[thread cancel];
// 获取所占的栈区大小
NSUInteger *size=[thread stackSize];
// 设置所占的栈区大小
[thread setStackSize:1024*5];
// 获取优先级
double x=thread.threadPriority;
// 设置优先级
[thread setThreadPriority:1000];
NSThread类方法
// 获取当前线程
NSThread *currentThread=[NSThread currentThread];
// 杀死当前线程
[NSThread exit];
// 阻塞当前线程10s
[NSThread sleepForTimeInterval:10];
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];
// 判断当前线程是否多线程
BOOL isMutiThread=[NSThread isMultiThreaded];
// 获取主线程
NSThread *mainThread=[NSThread mainThread];
// 判断当前线程是否是主线程
BOOL isMainThread=[NSThread isMainThread];
// 获取当前线程的调度优先级
// 调度优先级的取值范围是0.0~1.0,默认0.5,值越大,优先级越高。
double priority=[NSThread threadPriority];
// 设置调度优先级
[NSThread setThreadPriority:0.5];
// 即将进入多线程模式
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(willBecomeMuti:) name:NSWillBecomeMultiThreadedNotification object:nil];
// 进入单线程模式
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(becomeSigle:) name:NSDidBecomeSingleThreadedNotification object:nil];
// 线程退出
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(willExit:) name:NSThreadWillExitNotification object:nil];
GCD
同步/异步/主线程/全局线程
// 同步 1.主线程(串行)
dispatch_sync(dispatch_get_main_queue(), ^{
});
// 异步 2.全局线程(并行)
dispatch_async(dispatch_get_global_queue(0, 0), ^{
});
自定义线程队列
// 串行
dispatch_queue_t queue=dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
// 并行
dispatch_queue_t queue2=dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);
延迟
// 延迟2s执行
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC*2), dispatch_get_main_queue(), ^{
});
重复
// 重复3次执行
dispatch_apply(3, dispatch_get_main_queue(), ^(size_t x) {
});
onlyOne
// 仅执行一次
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
//
});
组 dispatch_group_t
//
dispatch_queue_t dispatchQueue = dispatch_queue_create("ted.queue.next", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group=dispatch_group_create();
dispatch_group_async(group, dispatchQueue, ^{
});
// group内所有队列完成后调用
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
});
同步锁
// dispatch_semaphore_t 信号量:用于同步
// 1.创建信号量(个数>=0) (放在代码块上)
dispatch_semaphore_t semaphore=dispatch_semaphore_create(0);
// 2.信号量个数+1 (放在代码块中)
dispatch_semaphore_signal(semaphore);
// 3. 等待直到信号量个数>0 (放在代码块下)
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 3. 10s内等待直到信号量个数>0
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 10));
同步锁
// dispatch_group_t同步
dispatch_group_t group=dispatch_group_create();
// 进入组
dispatch_group_enter(group);
dispatch_group_async(group, dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT), ^{
// 离开组
dispatch_group_leave(group);
});
// 等待直到离开组
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
栅栏
// dispatch_barrier_async先并发执行栅栏之前任务,在执行栅栏任务,在并发执行栅栏后的任务
dispatch_queue_t concurrentQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^(){
NSLog(@"dispatch-1");
});
dispatch_async(concurrentQueue, ^(){
NSLog(@"dispatch-2");
});
dispatch_barrier_async(concurrentQueue, ^(){
NSLog(@"dispatch-barrier");
});
dispatch_async(concurrentQueue, ^(){
NSLog(@"dispatch-3");
});
dispatch_async(concurrentQueue, ^(){
NSLog(@"dispatch-4");
});
NSOperation
NSOperation是对GCD的封装,相比GCD,NSOperation能处理大量的并发请求,更好的处理队列之间的依赖关系,缺点是比GCD稍慢效率低.
// 1.创建queue队列
NSOperationQueue *optionQueue=[NSOperationQueue new];
// 1.1设置queue最大并行数
[optionQueue setMaxConcurrentOperationCount:5];
// 2.添加blockOption到queue
// 创建option
NSBlockOperation *option=[NSBlockOperation blockOperationWithBlock:^{
}];
// 设置option代码块完成时调用
[option setCompletionBlock:^{
}];
// 添加option到queue
[optionQueue addOperation:option];
// 2.1 option添加依赖(option2完成后才能执行option1)
NSBlockOperation *option2=[NSBlockOperation blockOperationWithBlock:^{
}];
[option addDependency:option2];
// 删除依赖
[option removeDependency:option2];
// 2.添加blockOption到queue(匿名)
[optionQueue addOperationWithBlock:^{
}];
// 2. 添加InvocationOperation到queue
NSInvocationOperation *option=[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(method) object:nil];
// 添加option到queue
[optionQueue addOperation:option];
// 2. 添加option数组
[optionQueue addOperations:@[] waitUntilFinished:true];
queue 队列方法
// 获取 option数量(readOnly)
NSUInteger operationCount=optionQueue.operationCount;
// 获取 最大option数量
NSInteger maxConcurrentOperationCount=optionQueue.maxConcurrentOperationCount;
// 设置/获取 name
[optionQueue setName:@""];
NSString *name=optionQueue.name;
// queue是否暂停
BOOL isSuspended=[optionQueue isSuspended];
// queue暂停
[optionQueue setSuspended:true];
// queue取消所有option
[optionQueue cancelAllOperations];
// 阻塞直到queue所有option完成
[optionQueue waitUntilAllOperationsAreFinished];
option(NSOperation) 方法
// option设置name
[option setName:@""];
// option 开始
[option start];
// option 取消
[option cancel];
// 阻塞直到执行完毕
[option waitUntilFinished];
// 获取优先级
NSOperationQueuePriority queuePriority=option.queuePriority;
// 设置优先级
[option setQueuePriority:NSOperationQueuePriorityHigh];
/*
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
*/
// option 是否取消
BOOL isCanceled=[option isCancelled];
// option 是否正在执行
BOOL isExecuting=[option isExecuting];
// option 是否结束
BOOL isFinished=[option isFinished];
// option 是否异步
BOOL isAsy=[option isAsynchronous];
// 是否就绪
BOOL isReady=[option isReady];
// 获取依赖列表
NSArray<NSOperation *> *dependencies=option.dependencies;
自定义NSOperation
#import <Foundation/Foundation.h>
@protocol YTOperationCompletionDelegate
// 更新UI
-(void)opCompletion:(NSString *)str; // 参数看实际情况变化
@end
@interface YTMyOperation : NSOperation
@property (nonatomic,weak) id<YTOperationCompletionDelegate> dele;
@end
#import "YTMyOperation.h"
@implementation YTMyOperation
-(void)main{
//
[super main];
// 一些操作
//...
//
dispatch_async(dispatch_get_main_queue(), ^{
if(self.dele){
[self.dele opCompletion:@"complete"];
}
});
}
@end
blockOption(NSBlockOperation : NSOperation) 方法
// 添加
[option addExecutionBlock:^{
}];
InvocationOperation(NSInvocationOperation : NSOperation) 方法
3. block
block(即闭包,基于函数指针) 可用于传递数据
动画和gcd都用到:
[UIView animateWithDuration:3 animations:^{
}];
dispatch_async(dispatch_get_main_queue(),^{
});
- 声明(牢记)
@property (nonatomic,copy) void (^myBLock)(NSString *name);
- 方法
// 把声明中的block名字放到外面 就是类型
-(void)setMyBLock:(void (^)(NSString *))myBLock{
}
-(void (^)(NSString *))getMyBLock{
}
- 调用方法
// ^(参数类型 参名){}
[self setMyBLock:^(NSString *name) {
// return 10; 又返回值时+
}];
myBlock(@"name");
- 局部变量
// 局部变量
void (^myBlock)(NSString *name)=^(NSString *name){
};
- 重定义(起别名)
typedef void (^MyBlock) (NSString *name);
MyBlock myBlock=^(NSString *name){
};
4. KVC
KVC键值编码(k:键 V:值 C:编码)
是一种不通过存取方法而通过属性名间接访问属性的方式。
@interface NSObject(NSKeyValueCoding)
定义了KVC相关方法
@end
用于:
1.
2.
使用
PersonModel *personM=[PersonModel new];
// 首先 查找set方法->若没有则查找变量->若还没有则调用setValueforUndefinedKey->若没实现setValueforUndefinedKey则蹦
[personM setValue:@"张三丰" forKey:@"name"];
// 首先 查找get方法->若没有则查找变量->若还没有则调用valueforUndefinedKey->若没实现valueforUndefinedKey则蹦
NSString *name=[personM valueForKey:@"name"];
// 多级路径
[personM setValue:@"" forKeyPath:@"dog.name"];
int dogAge=[personM valueForKeyPath:@"dog.age"];
PersonModel.h
#import <Foundation/Foundation.h>
@interface PersonModel : NSObject
@property (nonatomic,copy) NSString *name;
@end
PersonModel.m
#import "PersonModel.h"
@implementation PersonModel
-(instancetype)initWithDic:(NSDictionary *)dic{
self=[super init];
if(self){
// 给模型所有属性赋值(等价于循环setValueForKey给属性赋值)
[self setValuesForKeysWithDictionary:dic];
}
return self;
}
// 找不到键时调用
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{}
// 覆写以下方法 做额外操作
-(void)setValue:(id)value forKey:(NSString *)key{
[super setValue:value forKey:key];
}
-(void)setValue:(id)value forKeyPath:(NSString *)keyPath{
[super setValue:value forKeyPath:keyPath];
}
-(void)setValuesForKeysWithDictionary:(NSDictionary<NSString *,id> *)keyedValues{
[super setValuesForKeysWithDictionary:keyedValues];
}
@end
5. KVO
KVO键值观察(k:键 V:值 O:观察)
监测某属性的变化(观察者模式的衍生)
PersonModel *personM=[PersonModel new];
/*
options :会观察到并传入observeValueForKeyPath方法
NSKeyValueObservingOptionOld 旧值
NSKeyValueObservingOptionNew 新值
NSKeyValueObservingOptionInitial 初始化
NSKeyValueObservingOptionPrior 分2次调用(在值改变之前和值改变之后)
*/
// 给personM添加观察者self,观察name属性的变化
[personM addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
// 移除观察者self
[personM removeObserver:self forKeyPath:@"name"];
// 观察方法(当所观察的东西发生变化时调用)
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
//NSKeyValueChangeNewKey NSKeyValueChangeOldKey
}
6. 内存管理机制
管理原则
OC基于引用计数管理对象的生命周期,每一个对象都绑定一个引用计数器。当通过alloc、new、copy创建对象时,引用计数为1,retain引用计数加1,release引用计数减1,当引用计数为0时则进行销毁(此时会调用delloc) 。
基本遵循:谁创建谁释放,谁引用谁管理。
管理方式(3种)
MRC手动引用计数
由程序员手动管理内存,当一个对象被引用时retain(如加入数组(由数组自动+1 -1),成为另一个对象的属性),当一个对象释放时release(移出数组,另一个引用该对象的对象被销毁).
ARC 自动引用计数
由编译器通过对代码的静态分析,在适当的地方添加retain和release(如在delloc、作用范围末尾、加入数组后、set方法中)
不能调用release retain retainCount autorelease,可以@autoreleasepool{}
ARC下采用非ARC编译某文件(混编):
需在BuildPharse | Compile File找到相应文件添加 -fobjc-arc 相反+ -fno-objc-arc)
__strong __weak
__weak typeOf(self) _weakSelf=self;
__strong typeOf(self) _strongSelf=_weakSelf;
AutoReleasePool 自动释放池 @autoreleasepool{}
当一个对象调用autorelease时,会将该对象放入自动释放池,在自动释放池结束时向所有自动释放池中的对象发送release进行销毁.
系统本身提供一个自动释放池,可以通过@autoreleasepool{}创建释放池.
方法返回对象时必须autorelease
7. Category
cmd+N 选Objective-C file新建类别文件
例:
文件NSArray+YTCusArray.h
#import <Foundation/Foundation.h>
@interface NSArray (YTCusArray)
@end
文件NSArray+YTCusArray.m
#import "NSArray+YTCusArray.h"
@implementation NSArray (YTCusArray)
@end
category的作用:
1.可以在不改变且不知道类的代码的情况下给类添加新方法。
2.可以将类的实现分散到不同的文件或框架中便于维护.
3.创建对私有方法的引用,实现非正式协议(即可选方法)。
不允许添加属性(可以通过运行时库实现添加属性)
可覆写原类中的方法。
多个类别覆写同一方法,则在Build Phases|Compile Sources中谁在下方谁优先级大
扩展可以添加属性,声明的方法必须实现(类别则可不实现)
8. 计时器
三种常见的定时器:NSTimer、CADisplayLink以及GCD Timer
NSTimer
方式一:
// 创建计时器(1s后执行)
NSTimer * timer = [[NSTimer alloc]initWithFireDate:[NSDate distantPast] interval:1 target:self selector:@selector(myLog:) userInfo:nil repeats:YES];
// 添加
[[NSRunLoop mainRunLoop]addTimer:timer forMode:NSDefaultRunLoopMode];
/*
消息处理机制(不断循环检测事件发生)
NSDefaultRunLoopMode 标准优先级(默认)优先级低。在滚动时NSTimer会失效,可添加到NSRunLoopCommonModes中。
NSEventTrackingRunLoopMode 则仅在滚动时有效(用于SV和别的控件的动画)
NSRunLoopCommonModes(二者合一)优先级高
*/
// 激活计时器
[timer fire];
// 创建并激活计时器
NSTimer *timer2=[NSTimer scheduledTimerWithTimeInterval:1.0 repeats:true block:^(NSTimer * _Nonnull timer) {
}];
// 创建并激活计时器
NSTimer *timer3=[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(handleTimer) userInfo:nil repeats:true];
// 暂停计时器
timer.fireDate=[NSDate distantPast];
// 继续计时器
timer.fireDate=[NSDate date];
// 销毁计时器
[timer invalidate];
timer=nil;
方式二:NSInvocation
// 1.初始化一个Invocation对象
//
NSInvocation * invo = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:@selector(handleTimer:)]];
// 或
NSInvocation *invo=[NSInvocation new];
[invo setTarget:self];
[invo setSelector:@selector(handleTimer:)];
// 2.创建NSTimer
// 创建NSTimer(1s,invo,是否重复)
NSTimer * timer = [NSTimer timerWithTimeInterval:1 invocation:invo repeats:YES];
// 加入主循环池中
[[NSRunLoop mainRunLoop]addTimer:timer forMode:NSDefaultRunLoopMode];
// 启动(开始循环)
[timer fire];
或
// 创建NSTimer并启动
NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1 invocation:invo repeats:YES];
9. protocol协议
protocol协议
只有方法声明,没有方法实现。
遵循协议(则拥有方法的声明),就需要实现必须实现的方法(@required 修饰)。
@protocol
@required // 默认
// 必须实现的方法...
@optional
// 可选实现的方法...
@end
10. 架构模式
常用
MVC
MVVM
MVC模式
M 表示Model模型层,数据模型(用来存储和传递数据)
V 表示View视图层,界面UI(用来显示数据)
C 表示Controller控制层,UIController(用来将数据显示到视图上,处理用户交互)
Model和View层不能直接通信
MVVM模式
M 表示Model模型层,数据模型(用来存储和传递数据)
V 表示View视图层,界面UI(用来显示数据)
VM 表示模型数据处理层,用来请求网络获取数据,并转换为UI所需的数据格式(简化C)
C 表示Controller控制层,UIController(用来将数据显示到视图上,处理用户交互)
网友评论