OC基础
Oc基础
1、#import的用途
1>#import与#include一样,拷贝文件的内容
2>可以自动防止文件的内容被重复拷贝
2、#import
NSLog(<#NSString * _Nonnull format,…#>)声明的函数
3、Foundation框架头文件路径
1>、Xcode右击,显示报内容
2>、/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks
4、主头文件
1>、最主要的头文件,名字一般跟框架名一样,包含框架中所有的其他文件
2>、Foundation框架的主头文件名称就是Foundation.h
3>、只要包含了Foundation的主头文件,就可以使用整个框架的东西
5、运行过程
1>编写代码 .m .c
2>编译 xxx.m xxx.c
3>链接 xxx.o xxx.o
4>运行 ./a.out
6、在使用类的时候,会将类加载到内存中
没有对象内部都有一个isa指针,这个指针指向类的方法列表
7、方法与函数的区别
方法:
1>对象方法以-号开头
2>方法的声明必须写在@interface和@end之间
方法的实现必须写在@implementation和end之间
3>对象方法只能又对象来调用
4>对象方法归对象和类拥有
函数:
1>函数可以写在文件的任意位置,函数归文件所有
2>函数的调用不依赖于对象
8、匿名对象
就是创建对象的时候,没有指针去指向这个对象
9、类方法和对象方法可以同名
类方法的执行效率比较高,但是类方法不能访问成员变量
10、工具类:基本没有任何成员变量的时候,里面的方法都是类方法。
11、继承
1>子类不能拥有和父类相同的成员变量
2>父类必须声明在子类之前
3>调用某个对象的方法,优先从该对象中查找,如果找不到再去父类中查找方法的实现
每个类中都有一个superClass指针,指向自己的父类,进而才可以去
super关键字的使用
1>super直接调用父类的方法
2>具体调用super的对象方法还是类方法,完全取决于当前的环境
3>子类中想先调用父类的方法之后,才去调用子类的方法时候,可以调用下super的方法
12、多态:父类的指针指向子类的对象 (狗 是DOG类型 ,它的父类是animation。所以一个狗的对象既可以用狗类的对象指针指向,也可以用动物类对象的指针指向)
1>没有继承就没有多态
2>如果函数的参数是父类,那么参数对象可以传子类、父类对象
3>局限性:父类类型的变量不能直接调用子类类型特有的方法
13、成员变量的作用域
局部变量、全局变量有自己的作用域,成员变量也不例外。
成员变量的作用域:
@public:在任何地方都能访问对象的成员变量
@private:只有当前类的对象方法中访问 或者是用set/get方法 (@implementation中声明的成员变量默认是private)
@protected:可以在当前类以及子类的对象中直接访问(默认就是@protected)或者是用set/get方法
@package:只要处于同一框架,就可以直接访问对象的成员变量
在@implementation中也能定义一个成员属性,但是默认是私有的
@interface和@implementation不能声明同名的成员属性
14、@property int age;会自动生成setAge和age方法的声明
@synthesizeage = _age;会自动声明setAge和age方法的实现,而且这句话会访问_age这个属性,如果不存在就会自动帮我们创建一个@private的_age属性。
@synthesizeage(如果不写=_age)会自动声明setAge和age方法的实现,而且这句话会访问_age这个属性,如果不存在就会自动帮我们创建一个@private的age属性。
Xcode4.3之后@property int age这一句代码就会帮我们生成 _age 以及setAge、age的方法声明与实现,但是这个方法生成的成员属性是私有的,不能让子类进行访问,如果想让子类进行访问,那么就需要在.h中声明声明一个protected的_age属性
如果手动实现了set方法,那么编译器就只会生成get方法和下划线属性
如果手动实现了get方法,那么编译器就只会生成set方法和下划线属性
如果手动生成了set和get方法,那么编译器就不会帮你生成下划线属性
15、构造方法
用来初始化对象的方法,是一个对象方法
重写init方法:
1>调用【super init】方法初始化父类的属性,并赋值给self
2>判断self是否为空,不为空则进行初始化属性操作
3>返回self
重写构造方法的目的:为了创建对象的时候,成员变量就有值
-(instancetype)init{
if(self=[superinit]){
_age =10;
}
returnself;
}
自定义构造方法:
1>一定是对象方法,以 - 开头
2>返回类型是 id类型
3>方法名以initWith开头
-(instancetype)initWithAge:(int)age{
if(self=[superinit]){
_age = age;
}
returnself;
}
16、修改Xcode创建文件的时候的一些配置属性
/Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates在这里面修改
17、分类:可以给某一类扩展一些方法,在不改变原来类的基础上,为类增加一写方法。
分类的使用注意:
1>只能增加方法,不能只能成员变量
2>分类方法的实现中可以访问原来类的成员变量
3>分类可以重新实现原来类中的方法,但是会覆盖掉原来类中的方法,会导致原来类中的方法无法使用
4>方法调用的优先级: 分类(最后编译的分类优先) —> 原类 —> 父类
18、
1>当程序启动时,就会加载项目中所有的类和分类,而且加载后会调用类和分类的+ (void)load{
}方法,只会调用一次
2>当第一次使用某个类的时候,会调用当前类的+(void)initialize
}方法
3>先加载父类,再加载子类,最后加载分类的(先调用父类的load方法,再调用子类的load方法)
先初始化父类,再初始化子类(先调用父类的initialize方法,再调用子类的initialize方法)
19、类也是个对象
类也是一个对象,是一个class类型的对象,简称类对象
Class类型的定义
typedefstructobjc_class * Class
类名就代表类对象,每个类只有一个对象
20、-(NSString *)description{}方法
默认情况下,利用NSLog和%@输出对象的时候,结果是<类名:内存地址>
1>会调用对象的description方法
2>拿到description方法的返回值展示到屏幕上
3>description默认返回的是“类名+内存地址”
+(NSString *)description{}方法,类的-(NSString *)description{}方法,默认返回的是类名
21、 NSLog(@"%s",__FILE__);
NSLog输出c语言字符串的时候,不可以包含中文
22、SEL
typedefstructobjc_selectorSEL
SEL s = NSSelectorFromString(@"test")
NSStringFromSelector(@selector(test))
_cmd代表当前方法
NSString *s = NSStringFromSelector(_cmd);
SEL其实是对方法的一个包装,去找到对应的方法地址就可以调用此方法
内存管理
1、局部变量的对象是放在栈中的,系统会管理他们的内存
一个oc对象的内存是放在堆中的,需要我们去手动管理他的内存
2、每个对象的内部都有四个字节来存储对象的内存空间
当一个对象alloc /new /copy的时候引用计数器会+1
给对象发送 retain 消息的时候,对象的引用计数器+1
给对象发送release消息的时候,对象的引用计数器-1
给对象发送 retainCount 消息的时候,返回对象的引用计数器的个数
3、对象销毁
当一个对象的引用计数器为0的时候,系统会给对象发送一个dealloc消息,一般重写此方法在其中做一些释放性的操作
4、野指针: 指针指向了一块已经释放的内存对象(这个对象成为僵尸对象)给野指针发送消息会报错 野指针经常报错为 EXC_BAD_ADRESS
空指针:没有指向任何对象的指针(存储的为nil/NULL/0) oc中给nil 发送消息不会报错
僵尸对象:所占用的内存已经被回收的对象,僵尸对象不能再继续使用
5、retain方法会返回对象本身
release方法不会有返回值
6、delloc
当对象要被回收的时候会调用、一定要调用【super delloc】,因为【super delloc】中也要做一些释放操作,而且这一句一定要写在最后
7、alloc/new/mutableCopy来创建对象的时候,引用计数器会+1,不使用这个对象的时候,需要将引用计数器-1
8、谁retain了,谁就要release
9、当你想使用或者占有某个对象的时候,需要对对象进行一次retain操作
当你不想使用或者占有某个对象的时候,需要对对象进行一个release操作
谁retain谁release
谁alloc谁release
10、假如person 有一个car属性
_car:直接访问成员属性
self->_car:直接访问成员属性
self.car :get方法
[self car]:get方法
11、MRC中的set方法
@interfacePerson : NSObject
{
Car *_car;
}
-(void)setCar:(Car *)car;
-(Car *)car;
@end
@implementationPerson
-(void)setCar:(Car *)car{
if(_car != car){ //这个判断是为了防止调用多次[self car]此时的car为同一个car,先给_car做一次release操作,此时car已经被释放了,然后再去调用【car retain】方法,这个相当于是给一个僵尸对象发送了一个retain消息。
[_car release]; //这个是为了防止在你给对象赋值新的car的时候,原有的car没有做release操作,而导致的原有的car不会被释放
_car =[car retain];//这段代码是为了谁拥有谁就要去retain
}
}
-(Car *)car{
return_car;
}
@end
12、内存管理的原则
1>只要调用了alloc,就必须调用release或者autorelease
2>set方法的代码规范
基本数据类型:
-(void)setAge:(int)age{
_age = age; 直接赋值
}
对象类型:
-(void)setCar:(Car*)car{
if(_car != car){
[_car release];//对旧对象进行release操作
_car =[car retain];//对新对象进行retain操作
}
}
3>delloc //对self所拥有的所有对象进行一次release操作
-(void)delloc{
[_car release];
//最后调用【super delloc】
[super delloc];
}
13、property默认生成的set和get方法是直接赋值assign
14、property(retain)就是相当于在set方法中先release旧值,再retain新值
15、property的参数有四个
1>set方法内存管理的相关参数
*retain:先release旧值,再retain新值
*assign:直接赋值
*copy
2>多线程管理
nonatomic :(性能高,set方法的时候不加线程锁)
atomic :(性能低,加线程锁)
3>是否只生成set和get方法
*readwrite:同时生成set和get方法的声明与实现
*readolny:只生成get的声明与实现
4>set和get的方法名称
setter:决定了set:的方法名,一般是需要带冒号的
getter:决定了getter方法名(一般用在bool类型)
例如:
@property(nonatomic,retain,readwrite,setter=setCarrrrr:,getter=carrrrr)Car *car;
16、
定义一个枚举
typedefenum{
SexMan,
SexWoman
} Sex;
定义一个结构体
typedefstruct{
intyear;
intmonth;
intday;
} Date;
17、@class只是告诉对象 这是一个类 , 但是不包含.h中的属性与方法
1>@class只是告诉编译器某个名称是一个类,例如@class person仅仅告诉编译器person是一个类
2>开发中引用一个类的规范
在.h中用@class来声明
在.m中用import来包含类的声明
3>两端循环引用的解决方案
一端用retain一端用assign
在.h中用@class来声明可以解决循环包含的错误
例如
#import <Foundation/Foundation.h>
#import "Person.h"
@interfaceCar : NSObject
@property(nonatomic,retain)Person *p;
@end
#import <Foundation/Foundation.h>
#import "Car.h"
@interfacePerson : NSObject
@property(nonatomic,retain)Car *car;
@end
这样会报错,编译不通过
#import <Foundation/Foundation.h>
@classPerson;
@interfaceCar : NSObject
@property(nonatomic,retain)Person *p;
@end
#import <Foundation/Foundation.h>
@classCar;
@interfacePerson : NSObject
@property(nonatomic,retain)Car *car;
@end
这样才不会报错,编译可以通过,然后在.m文件中用#import “”来包含头文件
这样做编译效率也比较高,如果按照第一种方式,一旦包含的类种的属性或者方法修改过了,就会造成每一个只要#import “”的.h都会重新编译一遍,造成效率比较低
18、autorelease的使用
autorelease方法会返回对象本身
autorelease 会把对象放在一个自动释放池中,当自动释放池被销毁的时候,会对池中的对象做一次release操作
@autoreleasepool{
Person *p = [[[Person alloc]init]autorelease];
}
会在池子结束的时候,对person对象调用一次release操作
autoreleasepool是可以嵌套使用的,而且autoreleasepool是放在栈内存中的,先进后出。
autoreleasepool的基本用法:
1>会将对象放入一个自动释放池中
2>当自动释放池销毁的时候,会对池中的对象进行一次release操作
3>会返回对象本身
4>调用完autorelease方法,对象的引用计数器不变,只有当autoreleasepool执行完毕之后,才会对池中的所有对象进行一次release操作
autorelease的好用:
1>不用关心对象释放的时间
2>不用关心什么调用对象的release
autorelease的使用注意:
1>占用较大内存的对象,不要随便的使用autorelease
2>占用较小内存的对象使用autorelease没有太大的影响
池子autoreleasepool的错误写法:
1> @autoreleasepool{
Person *p = [[[[Person alloc]init]autorelease]autorelease];//调用两次autorelease会出现野指针错误
}
2> @autoreleasepool{
Person *p = [[[Person alloc]init]autorelease];
[p release];
} 也会出现也指针错误
3> Person *p = [[[Person alloc]init]autorelease];这样写没有任何的意义,因为autorelease不放到autoreleasepool中是不会调用release的
自动释放池:
1>ios程序中也会创建自动释放池,这些自动释放池以栈的形式存在
2>当一个对象调用autorelease方法的时候,会将这个对象放到栈顶的释放池
自动释放池的创建方法:
#pragma mark ios5之前
NSAutoreleasePool *pool =[[NSAutoreleasePool alloc]init];
Person *p = [[[Person alloc]init]autorelease];
[pool release];
#pragma mark ios5之后
@autoreleasepool{
Person *p = [[[Person alloc]init]autorelease];
[p release];
}
}
19、
1>系统自带的方法没有alloc/new/copy,说明返回的对象都是autorelease的
例如 NSString *str =[NSString stringWithFormat:@"string"];
NSString *str1 =[NSString stringWithString:@"string"];
2>开发中经常提供一些类方法快速创建一个已经autorelease的对象
创建对象的时候,不要直接用类名,要用self,以满足子类的需求
例如
+(id)person{
return [[[selfalloc]init]autorelease];
}
内存管理的总结:
一、计数器操作:
1>retain +1;
2>release -1;
3>retaincount :获取计数器
二、set方法的内存管理
1>set的实现
-(void)setCar:(Car *)car{
if(_car != car){
[_car release];
_car =[car retain];
}
}
2>delloc方法的实现(不要直接调用delloc)
-(void)dealloc{
[_car dealloc];
[superdealloc];
}
三、property参数
oc对象:
@property(nonatomic,retain)类名*属性名;
非oc对象:
@property(nonatomic,assign)类型名称属性名;
四、@autoreleasepool
1、系统的方法中如果不带有alloc、new、copy,这些方法返回的对象都是已经autorelease过的
2、在开发过程中用类方法创建对象的时候,不要直接使用类名。可以用self(利用类方法快速创建一个autorelease对象)
ARC
ARC的判断准则:只要没有强指针指向对象,就会释放对象
1>ARC的特点是不允许调用retain/release/retainCount
2>允许重写delloc,但是不允许调用【super delloc】
3>property参数
*strong 成员变量是强指针 ,相当于之前retain(只适用于oc对象)
*weak成员变量是弱指针,相当于之前的assign(适用于oc对象)
*assign:适用于非oc对象
指针分为两种:
强指针:__strong 默认情况下就是强指针
弱指针:__weak 当弱指针指向的对象消失后,默认会指向nil
ARC与MRC的转换
1>mrc项目 转 arc
xcode -> edit ->convert -> to object ARC
2>查看项目当前是否为 ARC
buildSetting ->搜索auto -> objectc automatic reference counting ->yes代表arc No代表mrc
3>将项目中某个文件改为 arc或者mrc
build phases ->compile source ->-fno-objc-arc不需要arc或者-f-objc-arc需要ARC
当两端循环引用的时候,解决的方案
1、ARC
一端用strong一端用weak
2、非arc
一端用retain一端用assign
Block:
intsum(inta,intb){
returna+b;
}
-(void)viewDidLoad {
[superviewDidLoad];
int(*p)(int,int)= sum;
intsum = p(10,12);
NSLog(@"%d",sum);
}
指向函数的指针
int(^sumBlock)(int,int)=^(inta,intb){
returna+b;
};
sumBlock(100,100);
1>如何定义block变量
Int(^SumBlock)(int,int);
void(^myBlock)();
2>如何利用block封装代码块
^(int a,int b){
return a-b
};
^(){
NSLog(@“111111”);
};
3>block访问外部变量
*block内部可以访问外面的变量
*默认情况下,block内部不能修改外面的变量
*给局部变量上加上__block关键字,这个局部变量才可以在block内部修改
4>利用typedef定义block类型
typedefint(^MyBlock)(int,int);
以后就可以利用MyBlock这种类型来定义block变量
协议Protocol
1>基本用途
*可以用来声明一大堆方法
*只要某各类遵守了这个协议,就相当于拥有了这个协议的所有方法
*只要父类遵守了某个协议,就相当于子类也遵守了
2>格式
@protocol协议名称
//协议方法
@end
某个类遵守某个协议
@interface类名:父类<协议名称1,协议名称2>
@end
3>关键字
@required:这个方法是必须实现的(若不实现,编译器会发出警告)
@optional这个方法不一定实现
4>协议遵守协议
*一个协议可以遵守其他多个协议,多个协议之间用逗号隔开
*一个协议遵守了其他协议,就相当于拥有了其他以协议中的方法声明
@protocol协议名称<协议1,协议2>
@end
5>基协议
*nsobject是一个基类,最根本最基础的类,热河其他类都要继承它
*其实还有一个协议,名字也叫NSobject,它是一个基协议,最根本的协议
*nsobject协议中声明很多最基本的方法,比如description、retain、release等
*建议每一个新协议都要遵守nsobject协议
6>定义一个变量的时候,限制这个变量保存的对象遵守某个协议
类名<协议名称>*变量名;
id<协议名称>变量名;
NSObject*object;
Idobj2;
如果没有遵守协议,编译器会报警
7>@propety中声明的属性也可以用作一个遵守协议的限制
@property(nonatoimic,strong)类名<协议名称>*属性名
8>协议可以定义在单独的.h中,也可以定义在某个类型中
*如果这个协议只用在某个类中,应该定义在该类中
*如果某个协议用在很多类中,就应该把协议定义在单独的文件中
9>分类可以定义在单独.h和.m文件中,也可以定义在原类中
1>一般情况下,都是定义在单独文件
2>定义在原类中的分类,只要求看懂语法
集合中的对象的内存管理
1>当对象被添加到集合中的时候,对象的引用计数器会加一
2>当集合被销毁的时候,会对集合中的对象发送一次release消息
3>当对象被移除的时候,也会给对象发送一次release消息
@property内存管理的策略
1非ARC
1>copy:只用于nsstring/block
2>retain:除nsstring/block以外的oc对象
3>assign:基本数据类型、枚举、结构体(非oc对象)当两个对象相互引用的时候,一端用retain一端用assign
2ARC
1>copy:只用于nsstring/block
2>strong:除nsstring/block以外的oc对象
3>assign:基本数据类型、枚举、结构体(非oc对象)
4>weak当两个对象相互引用的时候,一端用retain一端用assign
自定义类实现copy操作
1>类遵守NSCopying协议
2>实现-(id)copyWithZone:(nullableNSZone *)zone;方法
3>在- (id)copyWithZone:(nullableNSZone *)zone方法中创建一个新的对象,并设置该对象数据与现有对象一致,并返回该对象
单例模式
alloc方法其实调用的是allocWithZone
Static修饰局部变量,只创建一次
static修饰的全局变量,只有当前文件才可以访问,并且只创建一次
1>永远只分配一块内存来创建对象
2>提供一个类方法,返回内部唯一的一个对象
3>最好保证init方法也只初始化一次
Dispath_once方法是线程枷锁的,能保证线程安全
非ARC单例模式的实现比ARC单例模式的实现多了
-(id)retain{
}
-(NSUInteger)retainCount{
}
-(onewayvoid)release{
}
-(instancetype)autorelease{
}
// ## : 连接字符串和参数
#if __has_feature(objc_arc)判断编译环境是ARC还是MRC
#define singleton_h(name)+(instancetype)shared##name;
#if __has_feature(objc_arc)// ARC
#define singleton_m(name)\
static id _instance;\
+(id)allocWithZone:(struct _NSZone *)zone \
{ \
static dispatch_once_t onceToken;\
dispatch_once(&onceToken,^{ \ dispatch_once线程安全,枷锁了
_instance =[super allocWithZone:zone];\
});\
return _instance;\
} \
\
+(instancetype)shared##name \
{ \
static dispatch_once_t onceToken;\
dispatch_once(&onceToken,^{ \
_instance =[[self alloc]init];\
});\
return _instance;\
} \
+(id)copyWithZone:(struct _NSZone *)zone \
{ \
return _instance;\
}
#else// 非ARC
#define singleton_m(name)\
static id _instance;\
+(id)allocWithZone:(struct _NSZone *)zone \
{ \
static dispatch_once_t onceToken;\
dispatch_once(&onceToken,^{ \
_instance =[super allocWithZone:zone];\
});\
return _instance;\
} \
\
+(instancetype)shared##name \
{ \
static dispatch_once_t onceToken;\
dispatch_once(&onceToken,^{ \
_instance =[[self alloc]init];\
});\
return _instance;\
} \
\
-(oneway void)release \
{ \
\
} \
\
-(id)autorelease \
{ \
return _instance;\
} \
\
-(id)retain \
{ \
return _instance;\
} \
\
-(NSUInteger)retainCount \
{ \
return 1;\
} \
\
+(id)copyWithZone:(struct _NSZone *)zone \
{ \
return _instance;\
}
#endif
控制器的生命周期
-(void)loadView{
[superloadView];
}
-(void)viewDidLoad{
[superviewDidLoad];
}
-(void)viewWillAppear:(BOOL)animated{
[superviewWillAppear:animated];
}
-(void)viewDidAppear:(BOOL)animated{
[superviewDidAppear:animated];
}
-(void)viewWillDisappear:(BOOL)animated{
[superviewWillDisappear:animated];
}
-(void)viewDidDisappear:(BOOL)animated{
[superviewDidDisappear:animated];
}
-(void)viewWillUnload{
[superviewWillUnload];
}
销毁view
-(void)viewDidUnload{
[superviewDidUnload];
}
-(void)didReceiveMemoryWarning{
[superdidReceiveMemoryWarning];
}
Block可以使用在定义之前声明的局部变量
inti=10;
void(^myBlock)(void)= ^(){
NSLog(@"%d",i);
};
i = 100;
myBlock();
打印的结果是 10;
1>在定义block的时候,会在block中建立当前局部变量的内容的副本(拷贝)
2>后续对该值进行修改不会影响block中的数值
3>如果需要在block中保持局部变量的数值变化,需要使用————block关键字
4>使用————block关键字之后,同样可以在block中修改变量的值
runtime
运行时机制:比较高级的特性,纯C语言
平时的oc代码——>C语言的运行时代码
默认情况下任何block的内存都是分配在栈中的,随时可能被回收。
对block进行一次copy操作就会把内存分配到堆中。
block会对代码块中的对象进行一次强引用,例如
self.block = ^{
self.age = 20;
}
这个时候,block会对self进行一次强引用,而block又是self的一个copy属性,也属于强引用,所以造成了循环引用;
因此当block中使用了对象的时候,需要判断是否造成了循环引用。如果造成了循环引用,就需要用——weak 来重新用指针指向对象
或者__unsafe_unretained 来重新修饰
例如
__unsafe_unretainedPerson *p2 = p1;
__weakPerson *p3 = p1;
__weaktypeof(self)weakSelf =self;
typeof(self) 就是用来判断类型,即等价于 self的类型*
KVC
利用kvc可以随意的修改一个对象的属性或者成员变量(并且私有的也可以)
Person *p1 =[[Person alloc]init];
Dog *dog =[[Dog alloc]init];
[p1.dog setValue:@"dogname" forKey:@"name"];
[p1.dog setValue:@"dogname" forKeyPath:@"dog.name"];
人对象有一个狗的属性,狗又有name的属性;
[p1.dog setValue:@"dogname" forKey:@"name”];中的key只能是属性名,否则会报错;
[p1.dog setValue:@"dogname" forKeyPath:@"dog.name"];
可以是路径,又可以是属性名
forKeyPath包含了for的功能,以后用forkeyPath
iOS中的多线程
1>一个NSTread对象就代表一条线程
2>创建和启动线程
NSThread *thread =[[NSThread alloc]initWithTarget:selfselector:@selector(run:)object:@"hah"];
[thread start];
3>主线程相关的用法
[NSThread mainThread];
[NSThread isMainThread];
[thread isMainThread];
4>设置线程的优先级
[NSThread threadPriority];查看当前线程的优先级
[NSThread setThreadPriority:0.5];设置当前线程的优先级
5>设置线程的名字
thread.name = @"线程一";
6>创建线程的时候就启动线程
[NSThread detachNewThreadWithBlock:^{
}];
[NSThread detachNewThreadSelector:@selector(run:)toTarget:selfwithObject:@"哈哈"];
7>隐shi创建
[selfperformSelectorInBackground:@selector(run:)withObject:@"哈哈”];
线程的状态
当线程在阻塞和死亡状态的时候,会退出可调度线程池中。但是内存还在。
线程状态的控制
最后的参数是说这个performSelector方法是否等待执行完毕,才继续跳出子线程操作
GCD
会自动帮我们创建线程
MRC中队列是需要释放的 当调用creat方法的时候,要调用dispatch_release(queue)方法去释放队列
在ios中凡是函数带有create、copy/new/retain等字眼,都需要在不需要使用这个数据的时候调用release操作
CFArrayRef array = CFArrayCreate(0,0,11,0);
CFRelease(array);在调用CF函数的时候,即使是在arc环境下也需要调用一下release操作
CF(core Facation)框架在ARC中也需要手动release
同步函数:
*串行队列:不开启新的线程,任务串行执行
*串行队列(主队列)会出现死锁
*并行队列:不开启新线程,任务串行执行
异步函数:
*串行队列 :开启一条新的线程,任务并行执行
*串行队列(主队列)不会开启新的线程
*并行队列: 开启新的线程,任务并行执行
dispatch_queue_t mainqueue = dispatch_get_main_queue();
dispatch_sync(mainqueue,^{
NSLog(@"这样会死锁");
});
dispatch_queue_t mainqueue = dispatch_get_main_queue();
dispatch_async(mainqueue,^{
NSLog(@"这样不会发生死锁");
});
因为异步函数虽然也是放在了主线程中,但是比较特殊,会将block中的代码缓后执行,所以不会出现死锁
延迟执行:
第一GCD:
//全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
//计算执行任务的时间
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW,(int64_t)(3.0 * NSEC_PER_SEC));
//在when的这个时间点去执行queue的任务
dispatch_after(when,queue,^{
});
第二:
[selfperformSelector:@selector(run:)withObject:@"hahha" afterDelay:3.0];
一次性代码
staticdispatch_once_t onceToken;
dispatch_once(&onceToken,^{
});
#define global_queue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)
#define main_queue dispatch_get_main_queue()
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group,global_queue,^{
//下载图片1
});
dispatch_group_async(group,global_queue,^{
//下载图片2
});
dispatch_group_notify(group,main_queue,^{
//当任务一 与 任务二 完成之后,回到主线程中去操作
});
NSOpertion:
多线程
1.NSThread
1> 开线程的几种方式
* 先创建,后启动
NSThread *thread =[[NSThread alloc]initWithTarget:selfselector:@selector(run)object:nil];
[thread start];
* 直接启动
[NSThread detachNewThreadSelector:@selector(run)toTarget:selfwithObject:nil];
[selfperformSelectorInBackground:@selector(run)withObject:nil];
2> 其他用法
NSThread *current =[NSThread currentThread];
+(NSThread *)mainThread;// 获得主线程
3> 线程间通信
performSelectorOnMainThread.....
2.GCD(重点)
1> 队列的类型
* 并发队列
获得全局的并发队列: dispatch_get_global_queue
* 串行队列
a.自己创建
dispatch_queue_create
b.主队列
dispatch_get_main_queue
2> 执行任务的方法类型
*同步(sync)执行
*异步(async)执行
3> 了解队列和方法的配合使用
4> 线程间通信
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
// 执行耗时的异步操作...
dispatch_async(dispatch_get_main_queue(),^{
// 回到主线程,执行UI刷新操作
});
});
5> 其他用法
dispatch_once
dispatch_after
dispatch_group_async\dispatch_group_notify
3.NSOperation
1> 基本使用
NSInvocationOperation
NSBlockOperation
2> NSOperationQueue(重点)
* 最大并发数设置
-(void)setMaxConcurrentOperationCount:(NSInteger)cnt;
* 设置依赖(面试题)
[operationB addDependency:operationA];// 操作B依赖于操作A
3>自定义Operation(了解基本流程)
4> 如何解决一张图片(一个url)重复下载的问题(面试题)