---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------
管理范围:任何继承了NSObject的对象,对其他基本数据类型(int、char、float、double、struct、enum等)无效
一、引用计数器
每个OC对象都有自己的引用计数器,是一个整数,表示“对象被引用的次数”
每个OC对象内部专门有4个字节的存储空间来存储引用计数器
1、 引用计数器的作用
a、当使用alloc、new或者copy创建一个新对象时,新对象的引用计数器默认就是1
b、当一个对象的引用计数器值为0时,对象占用的内存就会被系统回收
2、引用计数器的操作
a、retain、release方法的基本使用
1> retain:计数器+1;会返回对象本身
2> release:计数器-1;
3> retainCount:获取当前的计数器
4> dealloc:当一个对象要被回收的时候,就会调用
一定要调用[super dealloc],而且这句调用一定要放在最后面
b、概念
1> 僵尸对象: 所占用内存已经被回收的对象,僵尸对象不能再使用
2> 野指针: 指向僵尸对象(不可用内存)的指针,给野指针发送消息会报错:EXC_BAD_ACCESS
3> 空指针: 没有指向任何东西的指针 (存储的东西是nil、NULL、0),给空指针发送消息不会报错
2、 对象的销毁
a、当一个对象的引用计数器值为0时,那么它将被销毁,其占用的内存被系统回收
b、当一个对象被销毁时,系统会自动向对象发送一条dealloc消息
c、一般会重写dealloc方法,在这里释放相关资源,dealloc就像对象的遗言
d、一旦重写了dealloc方法,就必须调用[super dealloc],并且放在最后面调用
e、不要直接调用dealloc方法
f、一旦对象被回收了,它占用的内存就不再可用,坚持使用会导致程序崩溃(野指针错误)
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property int age;
@end
@implementation Person
// 当一个Person对象呗回收的时候,就会自动调用这个方法
- (void)dealloc
{
NSLog(@"Person对象被回收");
// super的dealloc一定要调用;而且放在最后面
[super dealloc];
}
@end
int main(int argc,const char * argv[])
{
@autoreleasepool {
Person *p = [[Personalloc] init];
NSUInteger c = [p retainCount];
NSLog(@"计数器:%ld",c);
// retain方法返回的是对象本身
// 2
[p retain];
// 引用计数器减1
// 1
[p release];
// 0
[p release];
// message sent todeallocated instance 0x100109a10
// 给已经释放的对象发送了一条-setAge:消息
// p.age = 10;
// p指向的是僵尸对象,应清空指针,指针p变成空指针
p = nil ;
// OC不存在空指针错误,给空指针发送消息不报错
[nil release];
// EXC_BAD_ACCESS :访问了一块坏的内存(已经被回收、已经不可用的内存)
// 野指针错误
[p release];
[p release];
[p release];
[p release];
}
return 0;
}
二、内存管理原则
1、只要还有人在用某个对象,那么这个对象就不会被回收
2、只要你想用这个对象,就让对象的计数器+1
3、当你不再使用这个对象时,就让对象的计数器-1
4、如果你通过alloc、new、或者[mutable]copy来创建对象,那么你必须调用release或autorelease
5、只要你调用了retain,你就用调用release
三、set方法的内存管理
(一)、内存管理代码规范
1、只要调用了alloc,必须有release(autorelease)
2、set方法的代码规范
1> 基本数据类型:直接复制
-(void)setAge:(int)age
{
_age = age;
}
2> OC对象类型
-(void)setCar:(Car *)car
{
// 1、判断是否是新传进来的对象
if (_car !=car) {
// 2、对旧对象做一次release操作
[_carrelease];
// 3、对新对象做一次retain操作
_car =[car retain];
}
}
3、dealloc方法的代码规范
1> 一定要写[super dealloc],而且放到最后面
2> 对当前对象(self)所拥有的其他对象做一次release操作
(二)、@property参数
1、set方法内存管理相关的参数
retain : release旧值,retain新值 (适用于OC对象类型)
assign : 直接赋值(默认,适用于非OC对象类型)
copy : release旧值,copy新值
2、是否要生成set方法
readwrite :同时生成setter和getter的声明、实现 (默认)
readonly :只会生成getter的声明和实现
3、多线程管理
nonatomic :性能高 (一般就用这个)
atomic :性能低 (默认)
4、setter和getter方法的名称
setter : 决定了set方法的名称,一定要有冒号:
getter : 决定了get方法的名称 (一般用在BOOL类型)
@interface Person : NSObject
@property (assign,readwrite,atomic)int age;
@property (retain,nonatomic)NSString *name;
@property (readonly)int height;
// 返回BOOL类型的方法名一般以is开头
@property (getter = isRich,setter = setRich:)BOOL rich;
@end
四、循环引用
1、@class的作用:仅仅告诉编译器,某个名称是一个类
@class Person; // 仅仅告诉编译器,Person是一个类
2、开发中引用一个类的规范
1> 在.h文件中用@class来声明类
2> 在.m文件中用#import来包含类的所有东西
除了父类,引用其他类的时候,用@class,不用#import,提高性能
3、两端循环引用解决方案
1> 一端用retain
2> 一端用assign
4、@class和 #import的区别
a、 #import方式会包含被引用类的所有信息,包括被引用类的变量和方法;@class方式只是告诉编译器这是类的声明,具体这个类里的信息,这里不需要知道,等实现文件中真正要用到时,才会真正去查看类中的信息
b、如果有上百个头文件都#import了同一个文件,或者这些文件依次被#improt,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍,而相对来 讲,使用@class方式就不会出现这种问题了
c、在.m实现文件中,如果需要引用到被引用类的实体变量或者方法时,@class还需要使用#import方式引入被引用类
五、autorelease
1、autorelease的基本用法
1> 会将对象放到一个自动释放池中
2> 当自动释放池被销毁时,会对池子里面的所有对象做一次release操作
3> 会返回对象本身
4> 调用完autorelease方法后,对象的计数器不变
2、autorelease的好处
1> 不用再关心对象的释放时间
2> 不用再关心什么时候调用release
3、autorelease的使用注意
1> 占用内存较大的对象不要随便使用autorelease
2> 占用内存较小的对象使用autorelease,没有太大影响
4、错误写法
1> alloc 之后调用了autorelease,又调用了release
@autoreleasepool {
// 1
Person *p=[[[Person alloc] init] autorelease];
// 0 会发生野指针错误
[p release];
}
2> 连续调用多次autorelease
@autoreleasepool {
// 释放池销毁时会进行两次release操作
Person *p=[[[[Person alloc] init] autorelease] autorelease];
}
5、自动释放池
1> 在ios程序运行过程中,会创建无数个池子,这些池子都是以栈结构存在(先进后出)
2> 当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池
6、自动释放池的创建方式
1> IOS 5.0前
NSAutoreleasePool*pool = [[NSAutoreleasePool alloc] init];
Person *p2 =[[[Person alloc] init] autorelease];
[pool release]; //[pool drain];
2> IOS 5.0 开始
@autoreleasepool
{
}
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (nonatomic,assign) int age;
@end
@implementation Person
- (void)dealloc
{
NSLog(@"Person--- dealloc");
[super dealloc];
}
@end
int main(int argc,const char * argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePoolalloc] init];
Person *p2 = [[[Personalloc] init]autorelease];
[pool release]; // [pooldrain];
@autoreleasepool
{ // {开始代表创建了释放池
// autorelease方法会返回对象本身
// 调用完autorelease方法后,对象的计数器不变
// autorelease会将对象放到一个自动释放池中
// 当自动释放池被销毁时,会对池子里面的所有对象做一次release操作
Person *p = [[[Personalloc] init]autorelease];
p.age = 10;
@autoreleasepool {
Person *p2 = [[[Personalloc] init]autorelease];
p2.age =20;
}
} // }结束代表销毁释放池
return 0;
}
7、autorelease 应用
a、系统自带的方法里面没有包含alloc、new、copy,说明返回的对象都是autorelease的
b、开发中经常会提供一些类方法,快速创建一个已经autorelease过的对象
创建对象时不要直接用类名,一般用self
+(id)person
{
return[[[self alloc] init] autorelease];
}
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (nonatomic,assign) int age;
+ (id) person;
+ (id) personWithAge:(int)age;
@end
@implementation Person
+ (id)person
{
return [[[selfalloc] init]autorelease];
}
- (void)dealloc
{
NSLog(@"%d岁的人被销毁了",_age);
[super dealloc];
}
+(id)personWithAge:(int)age
{
// Person *p = [[[Personalloc] init] autorelease];
Person *p = [selfperson];
p.age = age;
return p;
}
@end
@interface GoodPerson : Person
@property (nonatomic,assign) int money;
@end
@implementation GoodPerson
@end
int main(int argc,const char * argv[])
{
Person *p = [[Personalloc] init];
p.age =19;
[p release];
@autoreleasepool {
Person *p1 = [Personperson];
p1.age =20;
Person *p2 = [PersonpersonWithAge:30];
NSString *str = @"sf23"; // 默认autorelease
NSString *str2 = [NSStringstringWithFormat:@"age is %d",10];
NSNumber *num = [[NSNumberalloc] initWithInt:10];
[num release];
NSNumber *num2 = [NSNumbernumberWithInt:100];
GoodPerson *m = [GoodPersonpersonWithAge:340];
m.money = 100;
}
return 0;
}
六、ARC
自动引用技术,是编译器特性
ARC判断准则: 只要没有强指针指向对象,就会释放对象。
1、ARC特点
1> 不允许调用release、retain、retainCount
2> 允许重写dealloc,但不允许调用[super dealloc]
3> @property的参数
a、strong: 成员变量是强指针 (适用于OC对象类型)
b、weak : 成员变量是弱指针 (适用于OC对象类型)
c、assign: 适用于非OC对象类型
4> 以前的retain改为用strong,其他一切不变
2、指针分2种:
1> 强指针:默认情况下,所以指针都是强指针
2> 弱指针:__weak
3、当两端循环引用的时候,解决方案
1> ARC
一端用strong,一端用weak
2> 非ARC
一端用retain,一端用assign
Person.h
#import <Foundation/Foundation.h>
@class Dog;
@interface Person : NSObject
@property (nonatomic,strong) Dog *dog;
@property (nonatomic,strong) NSString *name;
@property (nonatomic,assign) int age;
@end
Person.m
@implementation Person
- (void)dealloc
{
NSLog(@"Personid dealloc");
}
@end
Dog.h
@class Person;
@interface Dog : NSObject
@property (nonatomic,weak) Person *person;
@end
Dog.m
@implementation Dog
- (void) dealloc
{
NSLog(@"Dog-- dealloc");
}
@end
main.m
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Dog.h"
int main(int argc,const char * argv[])
{
Person *p = [[Personalloc] init];
Dog *d = [[Dogalloc] init];
p.dog = d;
d.person = p;
return 0;
}
void test()
{
Person *p = [[Personalloc] init];
// Person *p2 = p; // 有这个代码,上面的对象就只有在程序结束的时候才会释放
// 创建新对象,释放上面的对象
p = [[Person alloc] init];
// 销毁p对象
// p = nil;
NSLog(@"---");
}
void test2()
{
// __weak Person *p = [[Person alloc]init]; //没有强指针指向,直接被释放,p变成空指针
Person *p = [[Personalloc] init];
// 弱指针 p2
__weak Person *p2 = p;
// 对象在此处被释放
p = nil;
p2 = nil;
NSLog(@"---");
}
void test3()
{
Person *p = [[Personalloc] init];
Dog *d = [[Dogalloc] init];
p.dog = d;
Dog *d2 = [[Dogalloc] init];
p.dog = d2;
NSLog(@"---");
}
七、Block
1、 如何定义block变量
int (^sumBlock)(int, int);
void (^myBlock) ();
2、 如何利用block封装代码
^(inta, int b){
returna + b;
};
^(){
NSLog(@"-------");
};
^{
NSLog(@"-------");
};
3、 block访问外面变量
1>block内部可以访问外面的变量
2> 默认情况下,block内部不能修改外面的局部变量
3> 给局部变量加上_block关键字, 这个局部变量就可以在block内部修改
4、 利用typedef定义block类型
typedef int (^MyBlock)(int, int);
// 以后就可以利用MyBlock这种类型来定义block变量
MyBlock b1,b2;
b1= ^(int a, int b){
return a - b;
};
MyBlock sumBlock = ^(int a, int b){
return a + b;
};
#import <Foundation/Foundation.h>
typedef int (^MyBlock)(int, int);
int main(int argc, const char * argv[])
{
MyBlock sumBlock = ^(int a, int b){
return a + b;
};
MyBlock minusBlock = ^(int a, int b){
return a - b;
};
MyBlock multiplyBlock = ^(int a, int b){
return a * b;
};
NSLog(@"%d - %d- %d",sumBlock(12,6), minusBlock(15,3), multiplyBlock(15, 3));
return 0;
}
// 没有返回值,没有形参的block
void test()
{
// block用来保存一段代码
// block的标志: ^
/*
block跟函数很像:
1、可以保存代码
2、有返回值
3、有形参
4、调用方式一样
*/
// 定义block变量
// 如果block没有形参,可以省略后面的()
void (^myBlock) () = ^{
NSLog(@"-------");
};
// 判断block变量调用block内部的代码
myBlock();
}
// 有返回值、有形参的block
void test2()
{
int (^sumBlock)(int, int) = ^(int a, int b){
return a + b;
};
int c = sumBlock(4, 5);
NSLog(@"%d",c);
// 用一个block输出N条横线
void (^lineBlock)(int) = ^(int n){
for (int i = 0; i < n; i++) {
NSLog(@"------");
}
};
lineBlock(5);
}
void test3()
{
int a =10;
__block int b = 20;
void (^block)();
block = ^{
// block内部可以访问外面的变量
NSLog(@"a = %d",a);
// 默认情况下,block内部不能修改外面的局部变量
// a = 20;
// 给局部变量加上_block关键字,这个局部变量就可以在block内部修改
b = 25;
};
block();
}
八、protocol
1、 协议的定义
@protocol 协议名称 <NSOject>
// 方法的声明列表...
@end
2、如何遵守协议
1> 一个类遵守协议
@interface 类名 : 父类名 <协议名称>
@end
2> 协议遵守协议
@protocol 协议名称 <其他协议名称1,其他协议名称2>
@end
3、协议中方法声明的关键字
1> @required 要求实现,如果没有实现就会发出警告(默认)
2> @optional 不要求实现,怎样都不会警告
4、定义一个变量的时候,限制这个变量保存的对象遵守某个协议
类名<协议名称> *变量名;
id<协议名称> 变量名;
NSOject<MyProtocol> *obj;
id<MyProtocol> obj2;
如果没有遵守对应的协议,编译器会警告
5、@property中声明的数学也可用做一个遵守协议的限制
@property (nonatomic, strong) 类名<协议名称> *属性名;
@property (nonatomic, strong) id<协议名称> 属性名;
@property(nonatomic, strong) Dog<MyProtocol> *dog;
@property (nonatomic, strong) id<MyProtocol> dog1;
6、协议可以定义在单独.h文件中,也可以定义在某个类中
1> 如果这个协议只用在某个类中,应该把协议定义在该类中
2> 如果这个协议用在很多类中,就应该定义在单独文件中
7、分类可以定义在单独.h和.m文件中,也可以定义在原来类中
1> 一般情况下,都是定义在单独文件中
2> 定义在原来类中的分类,只要求能看懂语法
MyProtocol.h
#import <Foundation/Foundation.h>
// 定义了一个名叫MyProtocol的协议
@protocol MyProtocol <NSObject>
//@required 要求实现,不实现就会发出警告
//@optional 不要求实现
- (void)test4;
@required // 默认@required
- (void)test;
- (void)test2;
@optional
- (void)test3;
@end
MyProtocol2.h
#import <Foundation/Foundation.h>
@protocol MyProtocol2 <NSObject>
- (void)haha;
@optional
- (void)haha1;
@end
MyProtocol3.h
// 一个协议遵守了另外一个协议,就可以拥有另外一个协议的所有方法声明
@protocol MyProtocol3 <MyProtocol>
- (void)hehe;
@end
Person.h
#import <Foundation/Foundation.h>
// #import"MyProtocol3.h"
@class Hashiq;
@protocol MyProtocol2 ;
@protocol MyProtocol3 ;
// 只要一个类遵守了某一份协议,就能拥有这份协议中的所有方法声明
// : 继承
// <>遵守协议
@interface Person : NSObject <MyProtocol3, MyProtocol2>
@property (nonatomic, strong) id<MyProtocol2> obj;
@property (nonatomic, strong) Hashiq *hashiq;
@end
Person.m
#import "Person.h"
#import "MyProtocol2.h"
#import "MyProtocol3.h"
@implementation Person
- (void)test
{
}
- (void)test2
{
}
- (void)test4
{
}
- (void)haha
{
}
- (void)hehe
{
}
@end
Dog.h
#import <Foundation/Foundation.h>
@protocol MyProtocol2 ;
@interface Dog : NSObject <MyProtocol2>
@end
Dog.m
#import "Dog.h"
#import "MyProtocol2.h"
@implementation Dog
- (void)haha
{
}
@end
Hashiq.h
#import "Dog.h"
@interface Hashiq : Dog
@end
Hashiq.m
#import "Hashiq.h"
@implementation Hashiq
@end
main.m
#import <Foundation/Foundation.h>
#import "MyProtocol.h"
#import "Person.h"
#import "MyProtocol3.h"
#import "Dog.h"
#import "Hashiq.h"
int main(int argc, const char * argv[])
{
Person *p = [[Person alloc] init];
p.obj =[[Dog alloc] init];
p.obj =[[Hashiq alloc] init];
return 0;
}
void test()
{
NSObject *obj = [[NSObject alloc] init];
NSObject *obj2 = @"sf23kfs";
// 要求obj3保存的对象必须是遵守MyProtocol这个协议
//NSObject<MyProtocol> *obj3 = [[NSObject alloc] init]; // [[NSObjectalloc] init]是没有遵守Myprototol协议的
NSObject<MyProtocol> *obj3 = [[Person alloc] init];
obj3 = nil;
id<MyProtocol> obj4 = [[Person alloc] init];
obj4 = nil;
// 需求obj5,保存的对象必须遵守MyPrototol3、并且继承了Person
Person<MyProtocol3> *obj5 = [[Person alloc] init];
}
---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------