[Objective-c 基础

A.内存存放、retain、release

1.栈内存:存放局部变量,运行超过变量作用域自后编译器自动回收
2.堆内存:存放对象(地址,对象实体)
3.对象的基本结构
(1)引用计数器(4字节):当计数器为0的时候被回收,初始化时为1
(2)当使用alloc、new、copy创建对象的时候,计数器默认是1
(3)给对象发送一条retain消息,计数器+1
(4)给对象发送一条release消息,计数器-1
(5)给对象发送一条retainCount,返回计数器数值
(6)对象被回收的时候,会调用dealloc方法;一般会重写dealloc方法,并最后调用[supper dealloc]
1 //当一个Person对象被系统回收的时候,就会调用此方法
2 - (void) dealloc
3 {
4    NSLog(@"Person对象被回收");
5     [superdealloc];
6 }
 
4.野指针错误(运行时错误)EXC_BAD_ACCESS
对象被回收之后,继续使用指针release对象,实际指针指向的对象已经不存在了(指向不可用内存)
解决:回收对象之后,清空指针,p = nil;
给空指针发送消息不会报错
OC中不存在空指针错误
 
5.僵尸对象管理 Zombie Object
不开启XCode中OC的Zombie Object管理的时候,对野指针调用对象方法、retainCount不会报错,计数器自动变回1
1     //0
2     [prelease];
3    
4     p.age=11;
5     [prun];
开启之后会发生运行时错误BAD_BREAKPOINT
开启方法:运行项目 -> Edit Scheme -> Diagnostic -> Enable Zombie Objects
 
概念:
(1)僵尸对象:所占内存已经被回收的对象,僵尸对象不能再用
(2)野指针:指向僵尸对象(不可用内存)的指针,给野指针发送消息会发送BAD_ACCESS运行时错误
(3)空指针:没有指向任何东西的指针(存储的时nil、NULL、0),给空指针发送消息不会报错
 
6.对象setter方法内存管理
(1)谁创建,谁release
(2)谁retain,谁release
(3)类的对象成员变量,在setter中retain,在dealloc中release
a.Person类声明
复制代码
1 @interfacePerson :NSObject
2 {
3    Book*_book;
4 }
5 
6 - (Book*) book;
7 - (void) setBook:(Book*) book;
8 
9 @end
复制代码
 
b.Person类实现
复制代码
 1 @implementationPerson
 2 - (Book*) book
 3 {
 4    return_book;
 5 }
 6 
 7 - (void) setBook:(Book*) book
 8 {
 9    if(_book!= book)
10     {
11         [_bookrelease];
12     }
13    _book= [bookretain];
14 }
15 
16 - (void) dealloc
17 {
18     [_bookrelease];
19    NSLog(@"Person被回收");
20     [superdealloc];
21 }
22 
23 @end
复制代码
 
c.main.c
复制代码
 1 intmain(intargc,constchar* argv[]) {
 2    // b=1
 3    Book*b = [[Bookalloc]init];
 4    // p1=1
 5    Person*p1 = [[Personalloc]init];
 6    
 7    // b=2
 8     [p1setBook:b];
 9    
10    // b=1
11     [brelease];
12     b =nil;
13    
14    // p1=0, b=0
15     [p1release];
16     p1 =nil;
17    
18    return0;
19 }
复制代码
 
 
B.@property的内存管理
1.使用@property默认生成的setter不会调用retain
 
2.使用@property参数可以自动管理指针引用数
例如retain:生成的setter自动retain一次,但是dealloc中的release还是需要手动编写
——————Peron.h-------------------
1 @interfacePerson :NSObject
2 @propertyintage;
3 @property(retain)Book*book;
4 
5 @end
 
—————Person.m--------------------
复制代码
1 @implementationPerson
2 - (void) dealloc
3 {
4     [_bookrelease];
5     [superdealloc];
6 }
7 @end
复制代码
 
3.@property的参数类型
同一个类型的参数只能使用一个
(1)内存管理相关参数
a. retain:release旧值,retain新值(适用于OC对象类型),注意dealloc中要手动释放一次
b. assign:直接赋值(默认,适用于非OC对象类型)
c. copy:release旧值,copy新值
 
(2)是否生成setter
a. readwrite:同时生成setter和getter的声明、实现
b. readonly:只会生成getter的声明、实现
@property(readonly)floatheight;
 
(3)多线程管理
a. nonatomic:性能高(一般手动指定这种)
b. atomic:性能低(默认)
@property(nonatomic) int age;
 
(4)setter和getter的名称,默认的setter、getter也有效
@property(nonatomic,getter=abc,setter=setAbc:) int weight;
注意写对setter方法,带冒号
 
    p.weight=10;//默认的setter
   NSLog(@"p.weight = %d", p.abc);
out:
p.weight = 10
 
 总结:
*setter:增加setter的名称,一定要加上冒号
*getter:增加getter的名称,一般用于增加BOOL类型的返回方法(is开头)
 
 
C.循环retain
1.循环声明
使用#import引入所有类信息的时候,当两个类互相持有对方的对象作为成员变量的时候,会出现变异错误:类不能被识别
——————Person.h—————————
1 #import<Foundation/Foundation.h>
2 #import"Card.h"
3 
4 @interfacePerson : NSObject
5 @property(nonatomic,retain) Card *card;
6 @end
 
—————Card.h--------------------------------
1 #import<Foundation/Foundation.h>
2 #import"Person.h"
3 
4 @interfaceCard :NSObject
5 @property(nonatomic,retain)Person*person;
6 @end
 
解决:
(1)使用@class声明一个类
仅仅告诉编译器某个名称是个类,忽略类中的其他信息
——————Person.h—————————
复制代码
1 #import<Foundation/Foundation.h>
2 
3 @classCard;
4 
5 @interfacePerson :NSObject
6 @property(nonatomic,retain)Card*card;
7 @end
复制代码
—————Card.h--------------------------------
复制代码
1 #import<Foundation/Foundation.h>
2 
3 @classPerson;
4 
5 @interfaceCard : NSObject
6 @property(nonatomic,retain) Person *person;
7 @end
复制代码
 
(2).若需要使用成员类中的其他信息,可以再.m文件中再#import
—>原则:在.m文件中才引入其他类的.h头文件
 
 
2.循环retain
互相持有,且使用了retain属性,不能释放
复制代码
 1 intmain(intargc,constchar* argv[]) {
 2    Person*p = [[Personalloc]init];
 3    Card*c = [[Cardalloc]init];
 4     p.card= c;
 5     c.person= p;
 6    
 7     [prelease];
 8     [crelease];
 9    return0;
10 }
复制代码
 
解决:其中一方使用retain属性,另一方使用assign手动进行回收
———Person.h-------------
复制代码
1 @implementationPerson
2 - (void) dealloc
3 {
4    NSLog(@"Person被回收");
5     [_cardrelease];
6     [superdealloc];
7 }
8 
9 @end
复制代码
 
————Card.h--------------------
复制代码
1 #import<Foundation/Foundation.h>
2 
3 @classPerson;
4 
5 @interfaceCard :NSObject
6 @property(nonatomic,assign)Person*person;
7 @end
复制代码
 
——————Person.m--------------
复制代码
1 @implementationPerson
2 - (void) dealloc
3 {
4    NSLog(@"Person被回收");
5     [_cardrelease];
6     [superdealloc];
7 }
8 
9 @end
复制代码
 
———----------Card.m—————--
复制代码
1 @implementationCard
2 - (void) dealloc
3 {
4    NSLog(@"Card被回收");
5 //    [_person release]; //没有retain,不必release
6     [superdealloc];
7 }
8 
9 @end
复制代码
 
D.autorelease方法
不使用ARC且在开启Enable Zombie Objects的时候,使用retainCount=0的对象指针会发生运行时错误
复制代码
 1  
 2     Person*p =nil;
 3    
 4    //创建自动释放池
 5    @autoreleasepool
 6     {//自动释放池开始
 7        // autorelease方法会返回对象本身
 8        // autorelease会将对象放到一个自动释放池
 9        //当自动释放池被销毁的时候,会对池子里面的所有对象做一次release操作
10         p = [[[Personalloc]init]autorelease];
11        
12         p.age=10;
13 
14     }//自动释放池销毁,所有对象release一次
15    
16 //    p.age = 20; //运行时错误
复制代码
 
@autoreleasepool可以嵌套使用
 
错误用法:
(1)alloc之后调用autorelease,又调用release
(2)多次调用autorelease
 
 
E.autorelease的实际应用
1.自定义包装了autorelease的构造、初始化方法
1 + (id) person
2 {
3    return[[[Personalloc]init]autorelease];
4 }
 
2.子类调用父类的这种包装方法的时候,返回的对象是父类对象,不符合需求
解决:
方式1:重写方法
方式2:创建对象的时候不使用类名,使用self进行alloc、init
1 + (id) person
2 {
3    return[[[selfalloc]init]autorelease];
4 }
如果一件事情你觉得难的完不成,你可以把它分为若干步,并不断寻找合适的方法。最后你发现你会是个超人。不要给自己找麻烦,但遇到麻烦绝不怕,更不要退缩。 电工查找电路不通点的最快方法是:分段诊断排除,快速定位。你有什么启示吗? 求知若饥,虚心若愚。 当你对一个事情掌控不足的时候,你需要做的就是“梳理”,并制定相应的规章制度,并使资源各司其职。
原文地址:https://www.cnblogs.com/wvqusrtg/p/4501623.html