-------------------------------
今日内容提要:
1. 复习
2. 自动释放池.
3. 作业讲解.微博练习.
4. ARC机制概述.
5. 第一个ARC程序
6. ARC机制下的单个对象的内存管理.
7. ARC机制下的多个对象的内存管理.
8. @property参数 strong与weak
9. ARC下的循环引用问题.
10.@property参数总结
11.ARC与MRC兼容.
12.MRC转换为ARC
13.分类的基本使用
14.非正式协议.
15.延展.
-----------------------------
复习:
0. 在MRC的模式下,我们只需要管理OC对象. 其他的数据都不需要我们进行管理.
决定OC对象什么时候被释放.
1. MRC下的内存管理原理.
每1个对象都有1个retainCount属性.叫做引用计数器.
占据8个字节,类型是unsinged long
引用计数器的作用:用来记录这个对象有多少个人正在使用.
当我们创建1个对象,这个对象的引用计数器为1.
如果有人使用这个对象,就让这个对象的引用计数器加1. 代表多1个人使用这个对象.
如果这个人不再使用这个对象,就让这个对象的引用计数器减1.代表少1个人使用这个对象.
当对象的引用计数器为0的时候.系统就会立即回收这个对象,并调用对象的dealloc方法.
关闭ARC的方式.
重点和难点:
什么时候为对象的引用计数器+1
什么时候为对象的引用计数器-1
2. 操作引用计数器的方式.
为对象发送retain消息. 对象的引用计数器就会加1. 这个消息的返回值是对象本身,
为对象发送release消息,对象的引用计数器就会减1.
为对象发送retainCount消息,就会返回对象的引用计数器的值.
在对象的引用计数器为0的时候,对象就会被系统立即回收,并调用这个对象的dealloc方法.
引用计数器的改变只能调用retain(++)、release(--),不能直接赋值.
3. 在MRC下,重写dealloc方法的规范.
必须要调用父类的dealloc方法,并且要放在最后调用. 别问为什么.
为什么? 因为子类中有父类的属性.而父类属性的release是放在父类的dealloc方法之中的.
为了能够释放所有的对象 必须要调用父类的dealloc方法.
[super dealloc];
4. 内存管理的原则
1). 有对象的创建,就必须要匹配1个release.
2). 只有在多1个人使用的时候,才为对象发送retain消息,只有当少1个人使用的时候才为对象发送release消息.
3). 谁retain 谁release
4). retain的次数要和release的次数匹配.
有始有终,有加就应该有减.
【曾经让某个对象的引用计数器加1.就应该在不使用这个对象的时候让对象的引用计数器减1】.
5. 野指针与僵尸对象.
1). 概念
野指针: 指针指向的对象已经被释放,这个指针就叫做野指针.
僵尸对象:1个被释放的对象,就叫做僵尸对象.
通过野指针去访问1个僵尸对象的时候.是有可能会出问题的.
[对象的回收的本质]:
是对象占用的空间系统可以分配给别的对象.
在未被分配给别的程序之前 其实啊这个对象还在的.数据也在的. 但是这块空间随时有可能分配给别的程序.
在未被分配给别的对象之前 其实还是可以访问的
一旦分配给别人就不能访问了.
僵尸对象就不应该被允许访问 因为被释放了.
开启僵尸对象检测.
2). 如何避免使用僵尸对象.
当指针为野指针的时候,将其赋值为nil.
3). 无法让僵尸对象复活.
6. 单个对象的内存泄露.
1).什么叫做内存泄露?
指对象没有被回收,该回收的时候而没有被回收,一直驻留在内存之中直到程序结束.
2).单个对象发生内存泄露的原因.
-> 有对象的创建,没有匹配的release
-> retain与release不匹配.
-> 在不恰当的时候,指针赋值为nil
-> 在方法中不当使用retain.
3).如何做到单个对象被正确释放
-> 有对象的创建就要有对象的release。
-> 有多少个retain就要有多少个release。
-> 不要轻易的为1个指针赋值为nil 除非指针是1个野指针.
-> 在方法中不要轻易的为参数retain
7. 多个对象的内存泄露.
演示.人与车.出现的内存泄露.
1). 当1个对象的属性是另外1个对象的适时候,如果对这个属性的set封装还是像我们基础班那样直接赋值.
这样是会出问题.会出现野指针的问题.
8. set方法的内存管理.
在MRC的模式下,如果属性的类型是OC对象类型的,这个属性的setter方法应该如何写.
1). 先retain传入的对象. 赋值给属性.在dealloc中release。
2). 重新赋值1个对象的时候 旧对象无法释放.
先release旧的对象,再retain新的对象.
3). 当多次赋值同1个对象的时候.就会出现僵尸对象 。
9. @property参数.
1). @proerty的作用
@property int age;
a. 生成1个私有的,int类型的属性_age; 是声明在@implementation的大括弧之中.
b. 生成getter、setter的声明.
c. 生成getter、setter的实现,
setter的实现: 将传递进来的值不做任何操作直接赋值给属性.
2), @property可以带参数. 不同的参数有不同的效果.
a,与多线程相关的.
atomic: 默认的.
nonatomic:
选择nonatomic. 因为效率高.
b. 和生成的set方法相关的参数.
retain 生成的set方法就是标准的MRC内存管理代码. 不再是直接赋值了
而是先判断新旧对象是否为同1个对象.如果不时 relase旧的 retain新的.
retain只是生成的set方法是标准的MRC内存管理代码. 不会自动的在dealloc中release
所以,我们还要在dealloc方法中手动的relase属性指向的对象.
assign: 默认值 生成的set方法中不做其他任何操作 直接赋值.
如果属性的类型是OC对象类型的,那么使用retain
如果属性的类型是非OC对象类型的 那么使用assign
c. 和生成的属性 只读、读写有关的参数.
readwrite: 默认值,getter setter同时生成.
readonly: 只生成getter
d. 修改生成getter setter方法的名字.
一般情况下别改, 只在1个地方.
当属性的类型是BOOL类型的时候 就更改getter的名字以is开头.
10. @class
两个头文件相互包含的时候 如果两边都使用#import来包含 就会出错 就会死循环.
其中1个头文件 不要使用#import指令 而是是@class 类名;
@class Person;
在.m文件中再去#import
11. 循环retain
当两个对象相互关联的时候.
人对象中有1个车
车对象中有1个人.
如果这两个@property都使用retain 就会出现泄漏.
解决方案: 1端retain 1端assign 使用assign的那1端不再需要在dealloc中release了.
02-自动释放池
1. 自动释放池的原理.
存入到自动释放池中的对象,在自动释放池被销毁的时候.会自动调用存储在该自动释放池中的所有对象的release方法.
可以解决的问题:
将创建的对象,存入到自动释放池之中. 就不再需要手动的relase这个对象了.
因为池子销毁的时候 就会自动的调用池中所有的对象的relase。
自动释放池的好处: 将创建的对象存储到自动释放池中,不需要再写release
2. 如何创建自动释放池.
@autoreleasepool
{
}
这对大括弧代表这个自动释放池的范围.
3. 如何将对象存储到自动释放池之中
在自动释放池之中调用对象的autorelease方法.就会将这个对象存入到当前自动释放池之中.
这个autorealse方法返回的是对象本身. 所以,我们可以这么写
@autoreleasepool
{
Person *p1 = [[[Person alloc] init] autorelease];
}
这个时候,当这个自动释放池执行完毕之后,就会立即为这个自动释放池中的对象发送1条release消息.
目前为止,我们感受到得autorelase的好处:
创建对象,调用对象的autorelase方法 将这个对象存入到当前的自动释放池之中.
我们就不需要再去relase 因为自动释放池销毁的时候 就会自动的调用池中所有对象的relase
[省略的是创建的对象的对应的那个release]
4. 使用注意
1). 只有在自动释放池中调用了对象的autorelease方法,这个对象才会被存储到这个自动释放池之中.
如果只是将对象的创建代码写在自动释放之中,而没有调用对象的autorelease方法.是不会将这个对象存储到这个自动释放池之中的.
2). 对象的创建可以在自动释放池的外面,在自动释放池之中,调用对象的autorelease方法,就可以将这个对象存储到这个自动释放池之中.
3). 当自动释放池结束的时候.仅仅是对存储在自动释放池中的对象发送1条release消息 而不是销毁对象.
4). 如果在自动释放池中,调用同1个对象的autorelease方法多次.就会将对象存储多次到自动释放池之中.
在自动释放池结束的时候.会为对象发送多条release消息.那么这个是就会出现僵尸对象错误.
所以,1个自动释放池之中,只autorelease1次,只将这个对象放1次, 否则就会出现野指针错误.
5). 如果在自动释放池中,调用了存储到自动释放中的对象的release方法.
在自动释放池结束的时候,还会再调用对象的release方法.
这个时候就有有可能会造成野指针操作.
也可以调用存储在自动释放池中的对象的retain方法.
6). 将对象存储到自动释放池,并不会使对象的引用计数器+1
所以其好处就是:创建对象将对象存储在自动释放池,就不需要在写个release了.
7). 自动释放池可以嵌套.
调用对象的autorelease方法,会讲对象加入到当前自动释放池之中
只有在当前自动释放池结束的时候才会像对象发送release消息.
5. autorelease的规范.
0). 创建对象,将对象存储到自动释放池之中. 就不需要再去手动的realse。
1). 类方法的第1个规范:
一般情况下,要求提供与自定义构造方法相同功能的类方法.这样可以快速的创建1个对象.
2). 我们一般情况下,写1个类. 会为我们的类写1个同名的类方法,用来让外界调用类方法来快速的得到1个对象.
规范:使用类方法创建的对象,要求这个对象在方法中就已经被autorelease过了.
这样,我们只要在自动释放池中, 调用类方法来创建对象, 那么创建的对象就会被自动的加入到自动释放中.
提供1个类方法来快速的得到1个对象.
规范
a. 这个类方法以类名开头. 如果没有参数就直接是类名 如果有参数就是 类名WithXX:
b. 使用类方法得到的对象,要求这个对象就已经被autorelease过了.
+ (instancetype)person
{
return [[[self alloc] init] autorelease];
}
这样,我们直接调用类方法.就可以得到1个已经被autorelease过的对象.
@autoreleasepool
{
Person *p1 = [Person person];
//这个p1对象已经被autorelase过了.不需要再调用autorelase
//这个p1对象就被存储到当前自动释放池之中.
}//当自动释放池结束.就会为存储在其中的p1对象发送release消息.
6. 实际上Apple的框架中的类也是遵守这个规范的.
通过类方法创建的对象都是已经被autorelease过的了.
所以,我们也要遵守这个规范. 类方法返回的对象也要被autorealse过.
以后,我们凡事创建对象是调用类方法创建的对象 这个对象已经是被autorelease过的了.
03-微博练习
在MRC
一、微博类 (Microblog)
属性:
* 文字内容
* 图片
* 发表时间 (可以用字符串表示NSString)
* 作者
* 被转发的微博
* 评论数
* 转发数
* 点赞数
//
// Microblog.h
//
#import <Foundation/Foundation.h>
#import "User.h"
@interface Microblog : NSObject
@property (nonatomic,retain)NSString * content;
@property (nonatomic,retain)NSString * picURL;
@property (nonatomic,assign)CZdata postTime;
@property (nonatomic,retain)User * user;
@property (nonatomic,retain)Microblog * microblog;
@property (nonatomic,assign)int review;
@property (nonatomic,assign)int forward;
@property (nonatomic,assign)int like;
@end
//
// Microblog.m
//
#import "Microblog.h"
@implementation Microblog
-(void)dealloc{
NSLog(@"微博被删除了");
[_content release];
[_picURL release];
[_user release];
[_microblog release];
[super dealloc];
}
@end
二、作者类 (User)
* 名称
* 生日
* 账号
//
// User.h
//
#import <Foundation/Foundation.h>
#import "Account.h"
@interface User : NSObject
@property (nonatomic,retain)NSString * nickname;
@property (nonatomic,assign)CZdata birthday;
@property (nonatomic,retain)Account * account;
@end
//
// User.m
//
#import "User.h"
@implementation User
-(void)dealloc{
NSLog(@"用户被注销了~");
[_nickname release];
[_account release];
[super dealloc];
}
@end
三、账号类 (Account)
* 账号名称
* 账号密码
* 账号注册时间
//
// Account.h
//
#import <Foundation/Foundation.h>
typedef struct{
int year;
int month;
int day;
}CZdata;
@interface Account : NSObject
@property (nonatomic,retain) NSString * accountId;
@property (nonatomic,retain) NSString * password;
@property (nonatomic,assign) CZdata regTime;
@end
//
// Account.m
//
#import "Account.h"
@implementation Account
-(void)dealloc{
NSLog(@"账户被封禁了~");
[_accountId release];
[_password release];
[super dealloc];
}
-(void)setAccountId:(NSString *)accountId andPassword:(NSString *)password andRegTime:(CZdata)regTime{
_accountId = accountId;
_password = password;
_regTime = regTime;
NSLog(@"恭喜!【%@】注册成功!",_accountId);
}
@end
//
// main.m
//
#import <Foundation/Foundation.h>
#import "Microblog.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Account * reg1 = [[Account new]autorelease];
reg1.accountId= @"wangdachui";
reg1.password = @"123456+";
reg1.regTime = (CZdata){2010,1,1};
User * u1 = [[User new]autorelease];
u1.nickname = @"王大锤";
u1.birthday = (CZdata){1980,12,12};
u1.account = reg1;
Microblog * m1 = [[Microblog new]autorelease];
m1.content =@"今天心情不错";
m1.picURL = @"goodDay.png";
m1.postTime =(CZdata){2010,8,8};
m1.user = u1;
m1.review = 100;
m1.forward = 290;
m1.like = 2000;
Account * reg2 = [[Account new]autorelease];
reg2.accountId= @"baike";
reg2.password = @"123456";
reg2.regTime = (CZdata){2010,1,1};
User * u2 = [[User new]autorelease];
u2.nickname = @"白客";
u2.birthday = (CZdata){1980,12,12};
u2.account = reg2;
Microblog * m2 = [[Microblog new]autorelease];
m2.content =@"今天心情确实不错";
m2.picURL = @"goodDay.png";
m2.postTime =(CZdata){2010,8,8};
m2.microblog= m1;
m2.user = u2;
m2.review = 100;
m2.forward = 290;
m2.like = 2000;
}
return 0;
}
----结构体和类的区别-----
1. 结构体只能封装属性,而类不仅可以封装属性还可以封装方法.
如果1个封装数据既有属性也有行为,只能用类.
2. 结构体变量分配在栈.OC对象分配在堆.
栈的空间相对较小.但是存储在栈中的数据访问效率相对较高.
堆的空间相对较大.但是存储在堆中的数据的访问效率相对较低.
如果1个封装数据只有属性.如果用结构体就会分配在栈 效率就会高.
如果使用类型 对象就分配在堆 效率相对就会低.
如果定义1个结构体,这个结构体中有很多个属性.那么这个时候结构体变量在栈中就会占据很大1块空间 反而会降低效率.
什么时候使用结构体: 1). 封装数据只有属性 2) 属性较少. 3个以下.
什么时候使用类: 1).封装数据既有属性也有行为. 2).只有属性 但是属性较多.
3. 结构体赋值是 直接赋值的值. 而对象的指针 赋值的是对象的地址.
04-ARC机制概述
1. 什么是ARC
Automatic Reference Counting,自动引用计数. 即ARC.
顾名思义:系统自动的帮助我们去计算对象的引用计数器的值,
可以说是WWDC2011和iOS5引入的最大的变革和最激动人心的变化.
ARC是新的LLVM3.0编译器的一项特性,使用ARC,可以说一举解决了广大iOS开着所憎恨的手动管理内存的麻烦.
在程序中使用ARC非常简单,只需要像往常那样编写代码.
只不过永远不要写retain、release、autorelease 永远要手动的调用 dealloc 这三个关键字就好,这是ARC的最基本的原则.
当ARC开启时, 编译器会自动的在合适的地方插入retain、release、autorelase代码.
编译器自动为对象做引用计数. 而作为开发者,完全不需要担心编译器会做错(除非开发者自己错用了ARC).
需要特别注意的是: ARC是编译器机制. 在编译器编译代码的时候,会在适时的位置加入retain、release和autorealse代码.
2. ARC机制下,对象何时被释放
本质: 对象的引用计数器为0的时候,自动释放.
表象: 只要没有强指针指向这个对象,这个对象就会立即回收.
3. 强指针与弱指针.
强指针: 默认情况下,我们声明1个指针 这个指针就是1个强指针.
我们也可以使用__strong来显示的声明这是1个强指针.
Person *p1; 这是1个强指针. 指针默认情况下都是1个强指针.
__strong Person *p2; 这也是1个强指针.使用__strong来显示的声明强指针.
弱指针: 使用__weak标识的指针就叫做弱指针.
无论是强指针还是弱指针,都是指针,都可以用来存储地址,这1点没有任何区别 。
都可以通过这个指针访问对象的成员.
唯一的区别就是在ARC模式下.他们用来作为回收对象的基准.
如果1个对象没有任何强类型的指针指向这个对象的时候,对象就会被立即自动释放
4. 确认程序是否开启ARC机制.
1).默认情况下,Xcode开启ARC机制.
2).ARC机制下,不允许调用retain、relase、retainCount、autorelease方法.
3).在dealloc中 不允许[super dealloc];
5. 演示第1个ARC案例
int main(int argc, const char * argv[])
{
@autoreleasepool
{
Person *p1 = [Person new];//p1是1个强指针.
//因为我们说过,每1个指针变量默认情况下都是1个强指针变量.
NSLog(@"------");
}//当执行到这里的时候.p1指针被回收,那么Person对象就没有任何
//强指针指向它了. 对象就在这被回收.
return 0;
}
05-第一个ARC程序
1. ARC下的单个对象的内存管理.
在ARC的机制下: 当1个对象没有任何的强指针指向它的时候 这个对象就会被立即回收.
1). 当指向对象的所有的强指针被回收的时候,对象就会被立即回收.
int main(int argc, const char * argv[])
{
@autoreleasepool
{
Person *p1 = [Person new];//p1是1个强指针.
Person *p2 = p1;//p2也是个强指针.p1和p2都指向Person对象.
//因为我们说过,每1个指针变量默认情况下都是1个强指针变量.
NSLog(@"------");
}//当执行到这里的时候.p1指针被回收,p2指针也被回收.那么Person对象就没有任何
//强指针指向它了. 对象就在这被回收.
return 0;
}
2).将所有指向对象的强指针赋值为nil的时候.对象就会被立即回收.
int main(int argc, const char * argv[])
{
@autoreleasepool
{
Person *p1 = [Person new];//p1是1个强指针.
//因为我们说过,每1个指针变量默认情况下都是1个强指针变量.
p1 = nil;//当执行到这句话的时候.p1赋值为nil.
//p1指针不再执行Person对象.
//Person对象没有被任何的指针所指向,所以.Person对象在这里被释放.
NSLog(@"------");
}
return 0;
}
这两种情况就叫做没有任何强指针指向对象.
1). 指向对象的所有强指针被回收掉
2). 指向对象的所有的强指针赋值为nil
2. 强指针与弱指针.
1). 强指针与弱指针的声明.
默认情况下,所有的指针都是强类型的,也就是说我们之前声明的指针变量都是强类类型的
p1指针是强类型的,因为默认情况下指针都是强类型的.
Person *p1 = [[Person alloc] init];
不过我们可以使用__strong来显示的标识指针是强类型指针.
__strong Person *p2 = [Person new];
这个时候p2指针类型是强指针类型的.其实写不写__strong都是强类型的指针.
指针类型也可以是弱指针类型.
使用__weak标识指针的类型是弱类型指针.
__weak Person *p3 = p2;
这个时候,p3指针就是1个弱类型的指针. p3弱指针也指向p2指针指向的对象.
在操作对象的时候,通过强指针或者弱指针都可以操作,没有任何区别.
2). ARC模式下的对象回收标准
ARC机制下释放1个对象的标准是: 没有任何强指针指向对象的时候,对象就会被释放.
如果这个时候有弱指针指向,也会被释放.
int main(int argc, const char * argv[])
{
@autoreleasepool
{
//使用__strong来标识p1指针是强类型的,其实不写__strong也是强类型的.
__strong Person *p1 = [[Person alloc] init];
//使用__weak标识指针p2的类型是弱类型指针.
__weak Person *p2 = p1;
//这个时候,p2指针和p1指针都指向Person对象.
//这个时候如果设置p1的值为nil
p1 = nil;
//这个时候Person对象只有被1个弱指针p2指向,没有任何强指针指向
//所以Person对象在这里被回收.
}
return 0;
}
3).最重要的1点:不能创建对象用1个弱指针存储这个对象的指针.
这样的话,刚创建出来的对象,就没有任何强指针指向,创建出来就会被回收.
int main(int argc, const char * argv[])
{
@autoreleasepool
{
//创建1个对象,将这个对象的地址赋值给1个弱指针
//后果就是创建出来的这个对象没有被任何强指针指向.
//刚创建出来就会被释放.
__weak Person *p1 = [[Person alloc] init];
}
return 0;
}
4). 在ARC机制下. 当对象被回收的时候. 原来指向这个对象的弱指针会被自动设置为nil
06-ARC机制下的多个对象的内存管理
1. ARC机制下的对象的回收的标准: 当没有任何强类型的指针指向对象的时候,这个对象就会被立即回收.
2. 强类型指针 弱类型指针.
3. 什么情况下叫做对象没有强指针向指向.
1). 指向对象的强指针被回收.
2). 指向对象的强指针被赋值为nil
4. 在ARC的机制下,@property参数不能使用retain
因为retain代表生成的setter方法是MRC的标准的内存管理代码.
而我们在ARC的机制下 不需要这些代码.
所以,在ARC机制下的setter方法 什么都不需要做.直接赋值就可以了.
5. ARC机制下,我们关注的重点.
当1个类的属性是1个OC对象的时候.这个属性应该声明为强类型的还是弱类型的.
很明显,应该声明为1个强类型的.
问题来了?
如何控制@property生成的私有属性,是1个强类型的还是1个弱类型的呢?
使用参数, strong和weak
@property(nonatomic,strong)Car *car;
代表生成的私有属性_car 是1个强类型的.
@property(nonatomic,weak)Car *car;
代表生成的私有属性_car 是1个弱类型的.
如果不写,默认是strong.
6. 使用建议.
1). 在ARC机制下.如果属性的类型是OC对象类型的.绝大多数场景下使用strong
2). 在ARC机制下.如果属性的类型不是OC对象类型的.使用assign
3). strong和weak都是应用在属性的类型是OC对象的时候. 属性的类型不是OC对象的时候就使用assign.
--------
在ARC机制下,将MRC下的retain换为strong
@property(nonatomic,strong)Car *car;
做的事情:
1). 生成私有属性.并且这个私有属性是strong
2). 生成getter setter方法的声明
3). 生成getter setter方法的声明
setter的实现:直接赋值.
07-ARC机制下的循环引用
在ARC机制下.当两个对象相互引用的时候.如果两边都使用strong 那么就会先内存泄露.
解决方案: 1端使用strong 1端使用weak
08-@property参数总结
1. 开发程序分为ARC和MRC
2. 与多线程相关的参数.
atomic : 默认值 安全,但是效率低下.
nonatomic: 不安全,但是效率高.
无论在ARC还是在MRC都可以使用.
使用建议: 无论是ARC还是MRC 都使用nonatomic
3. retain:
只能用在MRC的模式下.代表生成的setter方法是标准的内存管理代码.
当属性的类型是OC对象的时候.绝大多数情况下使用retain.
只有在出现了循环引用的时候1边retain 1边assign
4. assign:
在ARC和MRC的模式下都可以使用assign.
当属性的类型是非OC对象的时候 使用assign.
5. strong:
只能使用在ARC机制下.
当属性的类型是OC对象类型的时候,绝大多数情况下使用strong
只有出现了循环引用的时候, 1端strong 1端weak
6. weak:
只能使用在ARC机制下.
当属性的类型是OC对象的时候. 只有出现了循环引用的时候, 1端strong 1端weak
7. readonly / readwrite
无论是ARC还是MRC 都可以使用.
8. setter / getter
无论在ARC下还是在MRC下都可以改.
-----------------------------
在ARC机制下.原来使用retain的用strong
出现循环引用的时候. MRC: 1边retain 1边assign ARC: 1边strong 1边weak
09-MRC与ARC的兼容
1. 有可能会遇到的问题.
程序使用的是ARC机制开发的,但是其中的某些类使用的是MRC.
2. 使用命令. -fno-objc-arc
10-MRC转换为ARC
1. 可以将整个MRC程序,转换为ARC程序;(尽量不要用,没什么鸟用的功能。。。。。)
11-分类
1. 分类.
类别、类目、category
2. 写1个学生类:类中有很多个方法.
吃 喝 拉 撒 睡.... 基本行为
学习、敲代码、写书.... 学习
玩Dota 玩LOL 玩CF.... 玩
爬山、跑步、踢足球..... 运动
......
如果将这些方法都写在同1个类模块中.当然完全是可以的.
如果全都写在一个模块中,就显的很臃肿. 后期难以维护和管理.
默认情况下1个类独占1个模块.这个是将所有的成员都写在这1个模块中.就很难管理.
我们的想法: 那就让1个类占多个模块.将功能相似的方法定义在同1个模块中.
这样的好处: 方便维护和管理.
如何将1个类分成多个模块呢?
3. 分类:
1). 顾名思义: 将1个类分为多个模块.
2). 如何为1个类添加分类.
3). 会生成1个.h 和1个.m的模块.
a. 模块的文件名: 本类名+分类名.h 本类名+分类名.m
4). 添加的分类也分为声明和实现.
@interface 本类名 (分类名)
@end
代表不是新创建1个类.而是对已有的类添加1个分类. 小括弧中写上这个分类的名字.
因为1个类可以添加多个分类 为了区分每1个分类.所以分类要取名字.
@implementation Student (itcast)
@end
这是分类的实现.
4. 分类的使用.
1) 如果要访问分类中定义的成员,就要把分类的头文件引进来.
5. 分类的作用: 将1个类分为多个模块.
--------------------------------------------
使用分类注意的几个地方:
1. 分类只能增加方法,不能增加属性
2. 在分类之中可以写@property 但是不会自动生成私有属性. 也不会自动生成getter setter的实现.
只会生成getter setter的声明.
所以,你就需要自己写getter 和 setter的声明. 也需要自己定义属性 这个属性就必须在本类中.
3. 在分类的方法实现中不可以直接访问本类的真私有属性(定义在本类的@implementation之中)
但是可以调用本类的getter setter来访问属性.
本类的@property生成的私有属性,只可以在本类的实现中访问.
分类中不能直接访问私有属性 真.
分类可以使用 getter setter 来访问.
4. 分类中可以存在和本类同名方法的.
当分类中有和本类中同名的方法的时候,优先调用分类的方法.哪怕没有引入分类的头文件.
如果多个分类中有相同的方法,优先调用最后编译的分类.
------------------------------------
什么时候需要使用分类.
当1个类的方法很多很杂的时候. 当1个类很臃肿的时候.
那么这个时候我们就可以使用分类. 将这个类分为多个模块.将功能相似的方法写在同1个模块之中.
14-非正式协议
1. 分类的作用在于可以将我们写类分为多个模块.
可以不可以为系统的类写1个分类呢?
为系统自带的类写分类 这个就叫做非正式协议.
2. 分类的第2个作用:
为1个已经存在的类添加方法.
3. NSString类都挺好的. 就是差了1个方法.
统计字符串对象中有多少个阿拉伯数字.
分类的作用
1). 将臃肿的类分为多个模块 方便管理.
2). 扩展1个类.
15-补充(GC与ARC)
1. ARC机制垃圾回收机制的区别.
GC: 程序在运行的期间,有1个东西叫做垃圾回收器.不断的扫描堆中的对象是否无人使用.
Person *p1 = [Person new];
p1 = nil;
ARC: 不是运行时. 在编译的时候就在合适的地方插入retain......
插入的代码足以让对象无人使用的时候 引用计数器为0