iOS-OC内存管理
目标
1.【理解】内存管理
2.【掌握】第一个MRC程序
3.【掌握】内存管理的原则
4.【理解】野指针与僵尸对象
5.【理解】单个对象的内存管理
6.【理解】多个对象的内存管理
7.【掌握】set方法的内存管理
8.【掌握】@property参数
9.【掌握】@class关键字
10.【理解】循环retain
一、内存管理
程序在运行过程中会在堆空间创建大量的对象,当对象不再使用的时候,系统并不会自动释放堆空间中的对象(基本数据类型是由系统自动管理的)。如果一个对象创建并使用后没有得到及时释放,那么这对象会直到程序结束才会被释放,这样就会占用大量内存空间。其他高级语言如C#、Java都是通过垃圾回收机制(GC)来解决这个问题的,但在OC中并没有类似的垃圾回收机制,因此它的内存管理就需要由程序员手动维护。并且栈空间、BSS段、数据段、代码段中的数据都是由系统自动管理的,所以这些区域的数据不需要由程序员来管理,我们只需要管理堆空间中的数据,也就是OC对象的释放需要我们来管理。
引用计数器
OC中内存的管理是依赖对象引用计数器来进行的,在OC中每个对象内部都有一个与之对应的整数(retainCount),叫“引用计数器”。当一个对象在创建之后它的引用计数器默认值为1,如果一个对象的引用计数器为0,则系统会自动调用这个对象的dealloc方法来销毁这个对象。
内存泄露
一个对象在使用完没有回收,直到程序结束的时候才被回收,这种现象就叫做内存泄露。
如何操作对象的引用计数器?
调用对象的retain方法,对象的引用计数器就会+1。
调用对象的release方法,对象的引用计数器就会-1。
调用对象的retainCount方法,就会得到这个对象的引用计数器的值。
总结:
当我们要使用一个对象的时候,应该为这个对象发送一条retain消息。当我们不再使用一个对象的时候,就应该为这个对象发送一条release消息。当对象的引用计数器为0的时候,系统就会调用这个对象的dealloc方法来销毁这个对象,所以我们一般会重写dealloc方法来监控对象的销毁。
二、第一个MRC程序
OC内存管理的分为MRC(Manual Reference Counting)手动引用计数和ARC(Automatic Reference Counting)自动引用计数。见名知意,MRC是程序员自己手动记录对象的引用计数,而ARC则是系统自动记录对象的引用计数。由于ARC机制是在Xcode4.2后推出的,所以默认情况下Xcode是自动开启ARC机制的,我们要使用MRC就必须关闭ARC。

为了便于监控对象释放,我们经常需要重写dealloc方法。并且重写dealloc方法需要遵循一些规范,在子类的dealloc方法最后必须调用父类的dealloc方法。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
//Person.h文件
#import <Foundation/Foundation.h>
@interface Person : NSObject
@end
//Person.m文件
#import "Person.h"
@implementation Person
//重写dealloc方法
- (void)dealloc {
NSLog(@"人挂了...");
//在子类dealloc方法最后必须调用父类dealloc方法
[super dealloc];
}
@end
//main.m文件
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
//使用alloc创建对象,默认对象引用计数器的值为 1
Person *p = [[Person alloc] init];
//给对象发送release消息只是让引用计数器 -1,并不是一定会让对象引用计数器变为0
[p release];
//调用对象的dealloc方法,输出
//人挂了...
return 0;
}
|
三、内存管理的原则
只在有人使用这个对象的时候才为对象发送retain消息,只在一个人不再使用对象的时候才为对象发送release消息。遵守谁创建谁release,谁retain谁release的原则。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
//Person.h文件
#import <Foundation/Foundation.h>
@interface Person : NSObject
@end
//Person.m文件
#import "Person.h"
@implementation Person
//重写dealloc方法
- (void)dealloc {
NSLog(@"人挂了...");
//在子类dealloc方法最后必须调用父类dealloc方法
[super dealloc];
}
@end
//main.m文件
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
//使用alloc创建对象,引用计数器 +1
Person *p = [[Person alloc] init];
//给对象发送一条retain消息,引用计数器 +1
[p retain];
//给p对象发送一条retainCount消息,返回引用计数器的值
NSUInteger count = [p retainCount];
NSLog(@"count = %lu",count);
//给对象发送release消息只是让引用计数器 -1,并不是一定会让对象引用计数器变为0
[p release];
//再发送一条release,此时引用计数器就为0了
[p release];
//调用对象的dealloc方法,输出
//人挂了...了...
return 0;
}
|
注意:
1.为对象发送release消息不是让对象释放,而是让对象的引用计数器-1。
2.当对象引用计数器的值为0时,系统会立即调用对象的dealloc方法释放对象。
3.创建谁release,谁retain谁release。
四、野指针与僵尸对象
C语言中的野指针:一个指针变量指向一块随机的内存空间,也就是未初始化的指针变量,这个指针就叫做野指针。
OC语言中的野指针:指针指向的对象已经被释放了,这个指针就叫做野指针。
僵尸对象:已经被释放的对象,叫做僵尸对象,也就是通过野指针访问的对象就是僵尸对象。注意一旦一个指针成为了野指针,就不要通过野指针去访问对象的成员了。
每次通过一个指针去访问一个对象的时候,都会去检查这个对象是否是僵尸对象,如果是就报错。默认情况下Xcode是不会自动检测僵尸对象,那么如何开启Xcode对僵尸对象的自动检测?


当一个对象被回收后,就为指向这个对象的指针赋值nil,可避免访问僵尸对象。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
//Person.h文件
#import <Foundation/Foundation.h>
@interface Person : NSObject
- (void)eat;
@end
//Person.m文件
#import "Person.h"
@implementation Person
- (void)eat {
NSLog(@"人吃饭");
}
//重写dealloc方法
- (void)dealloc {
NSLog(@"人挂了...");
//调用父类dealloc方法
[super dealloc];
}
@end
//main.m文件
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
Person *p = [[Person alloc] init];
//release让引用计数器 -1,此时对象引用计数器为0.对象被销毁
[p release];
//p指针赋值nil
p = nil;
//给p对象发送消息不会报错,只会啥都不发生
[p eat];
return 0;
}
|
五、单个对象的内存管理
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
Person *p = [[Person alloc] init];//retainCount == 1
[p retain];//retainCount == 2
[p retain];//retainCount == 3
// //只有retain没有release
[p release];//retainCount == 2
[p release];//retainCount == 1
// //在不适当的时候为指针赋值nil
// p = nil;//对象还没释放,就将指针赋值nil
[p release];//retainCount == 0
return 0;
}
|
六、多个对象的内存管理
当一个对象的属性是另一个对象的时候,在这个属性的setter方法中,将传入的对象先retain一次,再赋值给属性。并在dealloc方法中对属性进行一次release。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
//Car.h文件
#import <Foundation/Foundation.h>
@interface Car : NSObject
- (void)run;
@end
//Car.m文件
#import "Car.h"
@implementation Car
-(void)run {
NSLog(@"车启动");
}
- (void)dealloc {
NSLog(@"车挂了...");
//调用父类dealloc方法
[super dealloc];
}
@end
//Person.h文件
#import <Foundation/Foundation.h>
#import "Car.h"
@interface Person : NSObject
@property Car *car;
- (void)drive;
@end
//Person.m文件
#import "Person.h"
@implementation Person
- (void)setCar:(Car *)car {
//retain传入的对象,因为多了一个指针(_car属性)指向这个传入的对象
//retain返回值是对象本身
_car = [car retain];
}
- (void)drive {
NSLog(@"车启动");
//调用_car的run方法
[self.car run];
}
- (void)dealloc {
NSLog(@"人挂了...");
//在人挂的时候让车也挂
[self.car release];
//调用父类dealloc方法
[super dealloc];
}
@end
//main.m文件
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
//实例化人和车对象
Person *p = [[Person alloc] init];
Car *bmw = [[Car alloc] init];
//给人一辆车
p.car = bmw;
//后创建的对象先release
[bmw release];
bmw = nil;
[p release];
p = nil;
return 0;
}
|
七、set方法的内存管理
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
//Car.h文件
#import <Foundation/Foundation.h>
@interface Car : NSObject
@property int speed;
- (void)run;
@end
//Car.m文件
#import "Car.h"
@implementation Car
-(void)run {
NSLog(@"车启动");
}
- (void)dealloc {
NSLog(@"时速为%d车挂了...",_speed);
[super dealloc];
}
@end
//Person.h文件
#import <Foundation/Foundation.h>
#import "Car.h"
@interface Person : NSObject
@property Car *car;
@property NSString *name;
- (void)drive;
@end
//Person.m文件
#import "Person.h"
@implementation Person
- (void)setCar:(Car *)car {
//判断传入对象是否和属性的旧值是同一对象
if (_car != car) {
//不是同一对象就release旧值
[_car release];
//retain新值
_car = [car retain];
}
}
- (void)drive {
//调用_car的run方法
[_car run];
}
- (void)dealloc {
NSLog(@"%@挂了...",_name);
//对象释放前将属性指向的对象release一次,表示不指向那个对象了
[_car release];
[super dealloc];
}
@end
//main.m文件
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
//实例化两个人对象
Person *p1 = [[Person alloc] init];//p1的retainCount == 1
Person *p2 = [[Person alloc] init];//p2的retainCount == 1
//分别为两个人对象的_name赋值
p1.name = @"jack";
p2.name = @"rose";
//实例化两个车对象
Car *bmw = [[Car alloc] init];//bmw的retainCount == 1
Car *benz = [[Car alloc] init];//benz的retainCount == 1
//分别为两个车对象的_speed赋值
bmw.speed = 100;
benz.speed = 80;
//p1指向bmw
p1.car = bmw;//bmw的retainCount == 2
//p2也指向bmw
p2.car = bmw;//bmw的retainCount == 3
//p2不指向bmw了,指向benz
p2.car = benz;//bmw的retainCount == 2 , benz的retainCount == 2
[benz release];//benz的retainCount == 1
[p2 release];//p2的retainCount == 0 调用了p2的delloc方法,再调用benz的release. benz的retainCount == 0 所以rose挂了,benz也挂了
[bmw release];//bmw的retainCount == 1
[p1 release];//p1的retainCount == 0 调用了p1的delloc方法,再调用bmw的release. bmw的retainCount == 0 所以jack挂了,bmw也挂了
/* 输出
rose挂了...
时速为80车挂了...
jack挂了...
时速为100车挂了...
*/
return 0;
}
|
八、@property参数

@property的参数分为三类,中间用逗号分隔,每类参数可以从上图三类参数中任选一个。如果不进行设置或者只设置其中一类参数,程序会使用三类中的各类的默认参数,默认参数:(atomic,readwrite,assign)。
格式:@property ([参数1,参数2,参数3]) 数据类型 名称; // []里表示可选
一般情况下如果在多线程开发中一个属性可能会被两个及两个以上的线程同时访问,此时可以考虑atomic属性,否则建议使用nonatomic,不加锁,效率较高。readwirte方法会生成getter、setter两个方法,如果使用readonly则只生成getter方法。关于setter方法处理需要特别说明,假设我们定义一个属性a,这里列出三种方式的生成代码:
assign,用于基本数据类型
|
1
2
3
|
- (void)setA:(int)a {
_a = a;
}
|
retain,通常用于非字符串对象
|
1
2
3
4
5
6
|
- (void)setA:(Car *)a {
if(_a != a){
[_a release];
_a = [a retain];
}
}
|
copy,通常用于字符串对象
|
1
2
3
4
5
6
|
- (void)setA:(NSString *)a{
if(_a != a){
[_a release];
_a = [a copy];
}
}
|
我们可以在参数中指定@property生成的setter、getter的方法名称,一般情况下,当属性的类型是一个BOOL类型的时候,我们可以指定getter方法名以is开头,这样增强代码可阅读性。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@property (nonatomic,assign,readwrite,getter=isRich) int rich;
//生成的setter、getter方法声明
- (void)setRich:(BOOL)rich;
- (BOOL)isRich;
//生成的setter、getter方法实现
- (void)setRich:(BOOL)rich {
}
- (BOOL)isRich {
}
|
实际应用案例
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
//Book.h文件
#import <Foundation/Foundation.h>
@interface Book : NSObject
@property (nonatomic, copy) NSString *bookName;
@end
//Book.m文件
#import "Book.h"
@implementation Book
- (void)dealloc {
NSLog(@"<<%@>>被烧了",_bookName);
[_bookName release];
[super dealloc];
}
@end
//Student.h文件
#import <Foundation/Foundation.h>
#import "Book.h"
@interface Student : NSObject
@property (nonatomic, retain) Book *book;
@end
//Student.m文件
#import "Student.h"
@implementation Student
- (void)dealloc {
NSLog(@"<<%@>>",_book.bookName);
[_book release];
[super dealloc];
}
@end
//main.m文件
#import <Foundation/Foundation.h>
#import "Student.h"
int main(int argc, const char * argv[]) {
Student *stu = [[Student alloc] init];//stu的retainCount == 1
Book *book = [[Book alloc] init];//book的retainCount == 1
stu.book = book;//book的retainCount == 2
stu.book.bookName = @"风流少女";
[book release];//book的retainCount == 1
[stu release];//stu的retainCount == 0,调用stu的dealloc方法,再调用book的release,则book的retainCount == 0
/*
输出
学生挂了
<<风流少女>>被烧了
*/
return 0;
}
|
九、@class关键字
当两个类相互包含的时候,就会造成死循环。在.h文件中不能包含和自己关联的那个类的头文件,因为一旦包含就会造成死循环。应该使用@class来声明一个类存在,如果在.m文件中需要使用用@class声明的类的成员时,就在.m文件中包含即可。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
//Book.h文件
#import <Foundation/Foundation.h>
@class Student;//如果不需要使用类中的成员可以使用@class告诉编译器,Student是一个类
@interface Book : NSObject
@property (nonatomic, copy) NSString *bookName;
@property (nonatomic, assign) Student *owner;
- (void)showOwner;
@end
//Book.m文件
#import "Book.h"
#import "Student.h"
@implementation Book
- (void)showOwner {
//使用了Student类的成员_name,直接包含头文件即可
NSLog(@"%@的主人是%@",self.bookName,self.owner.name);
}
- (void)dealloc {
NSLog(@"书挂");
[_bookName release];
[super dealloc];
}
@end
//Student.h文件
#import <Foundation/Foundation.h>
@class Book;//如果不需要使用类中的成员可以使用@class告诉编译器,Book是一个类
@interface Student : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, retain) Book *book;
- (void)showBook;
@end
//Student.m文件
#import "Student.h"
#import "Book.h"
@implementation Student
- (void)showBook {
//使用了Book类的成员_bookName,直接包含头文件即可
NSLog(@"%@的书名叫%@",self.name,self.Book.bookName);
}
- (void)dealloc {
NSLog(@"学生挂");
[_name release];
[_book release];
[super dealloc];
}
@end
|
十、循环retain
当两个类互相引用的时候,如果两个类的@property的参数都是retain就会出现循环引用造成内存泄露。解决办法是一个类使用retain,另一个类使用assign。使用assign的类不用在dealloc方法中对那个属性release。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
//Book.h文件
#import <Foundation/Foundation.h>
@class Student;//告诉Book,Student是一个类
@interface Book : NSObject
@property (nonatomic, copy) NSString *bookName;
@property (nonatomic, assign) Student *owner;//使用assign
@end
//Book.m文件
#import "Book.h"
@implementation Book
- (void)dealloc {
NSLog(@"<<%@>>释放",self.bookName);
[super dealloc];
}
@end
//Student.h文件
#import <Foundation/Foundation.h>
@class Book;//告诉Student,Book是一个类
@interface Student : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, retain) Book *book;//使用retain
@end
//Student.m文件
#import "Student.h"
@implementation Student
- (void)dealloc {
NSLog(@"<<%@>>释放了",self.name);
[_book release];
[super dealloc];
}
@end
//main.m文件
#import <Foundation/Foundation.h>
#import "Book.h"
#import "Student.h"
int main(int argc, const char * argv[]) {
Book *book = [[Book alloc] init];//book的retainCount == 1
book.bookName = @"风流少女";
Student *student = [[Student alloc] init];//student的retainCount == 1
student.name = @"牛逼哥";
book.owner = student;//因为是assign,所以不会retain传入的student,则student的retainCount == 1
student.book = book;//因为是retain,会retain传入的book,则book的retainCount == 2
[student release];//student的retainCount == 0,会调用student的dealloc方法,再调用一次book的release。则book的retainCount == 1
student = nil;
[book release];//book的retainCount == 0
book = nil;
/* 输出
<<牛逼哥>>释放了
<<风流少女>>释放
*/
return 0;
}
|
iOS-OC内存管理的更多相关文章
- iOS - OC 内存管理
1.OC 基本内存管理模型 1.1 自动垃圾收集 在 OC 2.0 中,有一种称为垃圾收集的内存管理形式.通过垃圾收集,系统能够自动监测对象是否拥有其他的对象,当程序执行需要空间的时候,不再被引用的对 ...
- iOS学习17之OC内存管理
1.内存管理的方式 1> iOS应用程序出现Crash(闪退),90%的原因是因为内存问题. 2> 内存问题 野指针异常:访问没有所有权的内存,如果想要安全的访问,必须确保空间还在 内存泄 ...
- 【0 - 1】OC内存管理
一.内存管理概述 垃圾回收机制(GC):由系统管理内存,程序员不需要管理. OC中的垃圾回收:在OC2.0版加入垃圾回收. OC与iOS:OC有垃圾回收机制,但是iOS屏蔽了这个功能.原因:iOS运行 ...
- OC 内存管理机制总结
OC 内存管理机制总结 一:OC内存管理机制目前分为两块,其一自动内存管理机制,其二手动内存管理机制: 1.首先我们从自动内存管理机制讲起: 1)什么是自动内存管理机制,自动内存管理机制就是程序中所创 ...
- iOS之内存管理(ARC)
iOS的内存管理,相信大家都不陌生,之前是使用的MRC,由开发人员手动来管理内存,后来使用了ARC,来由系统管理内存.本文主要讲讲Autorelease,Core Foundation对象在内存管理方 ...
- OC内存管理总结,清晰明了!
<span style="font-size:18px;">OC内存管理 一.基本原理 (一)为什么要进行内存管理. 由于移动设备的内存极其有限.所以每一个APP所占的 ...
- iOS的内存管理和引用计数规则、Block的用法以及三种形式(stack、malloc、global)
学习内容 iOS的内存管理和引用计数规则 内存管理的思考方式 自己生成的对象自己持有 非自己生成的对象自己也能持有 自己持有的对象不需要时释放 非自己持有的对象不能释放 ARC有效时,id类型和对象类 ...
- 理解 iOS 的内存管理
远古时代的故事 那些经历过手工管理内存(MRC)时代的人们,一定对 iOS 开发中的内存管理记忆犹新.那个时候大约是 2010 年,国内 iOS 开发刚刚兴起,tinyfool 大叔的大名已经如雷贯耳 ...
- iOS ARC内存管理
iOS的内存管理机制,只要是iOS开发者,不管多长的时间经验,都能说出来一点,但是要深入的理解.还是不简单的.随着ARC(自动管理内存)的流行.iOS开发者告别了手动管理内存的复杂工作.但是自动管理内 ...
- OC内存管理基础
OC 内存管理基础 一. retain和release基本使用 使用注意: 1.你想使用(占用)某个对象,就应该让对象的计数器+1(让对象做一次retain操作) 2.你不想再使用(占用)某个对象,就 ...
随机推荐
- 开源PLM软件Aras详解一 安装
对于Aras,一定很多人陌生,Aras Innovator是开源的PLM系统,优点就是免费license,没有节点限制,最棒的是基于SOAP,扩展性非常强,但是国内很多想入手的人入门很难, 关于Ara ...
- AsyncTask的缺陷以及解决方法
1.AsyncTask常用于进行耗时操作,完成后更新主线程的UI. 2.缺陷:AsyncTask中维护着一个长度为128的线程池,同时可以执行5个工作线程,还有一个缓冲队列,当线程池中已有128个线程 ...
- IOS开发涉及有点概念&相关知识点
前言,IOS是基于UNIX的,用C/C+/OC直通系统底层,不想android有个jvm. 首先还是系统架构的分层架构 1.核心操作系统层 Core OS,就是内存管理.文件系统.电源管理等 2.核心 ...
- Java集合之TreeMap
Map的单元是对键值对的处理,之前分析过的两种Map,HashMap和LinkedHashMap都是用哈希值去寻找我们想要的键值对,优点是由O(1)的查找速度. 那如果我们在一个对查找性能要求不那么高 ...
- canvas事件
canvas的事件只能监听在canvas标签上,而不嗯那个想其他dom元素那样,所以针对canvas里面的图形监听事件是比较麻烦的. 通过isPointInPath(x,y); 这个方法用于判断某一 ...
- JS倒计时网页自动跳转代码
<title>JS倒计时网页自动跳转代码</title> <script language="JavaScript" type="text/ ...
- cocos2d-x 之 CCArray 的遍历(3)
cocos2d-x中CCArray的遍历,需要几个宏.现代C++程序设计建议尽量不要使用宏,所以数组的遍历也可以自己写. 但cocos2d-x官方已经提供了几个方便数组遍历的几个宏,用好了,能方便许多 ...
- Dell R710、720等系列类服务器 U盘安装centos6.5 操作系统
一般全新服务器创建系统的时候,没做raid 会报错,如下: 解决: 开机启动时按F10,进入下面界面. 在LC设置-语言和键盘设置选项里可以选择界面显示的语言 在界面主页选项里选择"配置RA ...
- MVC过滤器
MVC的每一个请求都会给相应的控制器的对应行为方法处理,那么想在这些处理的前 前后后增加一些额外的逻辑处理,因此过滤器的作用就来了 MVC支持的过滤器类型有四种,分别是:Authorization(授 ...
- 比较有价值的参考文档-----------------URL
http://www.wendangku.net/doc/f7b4349108a1284ac85043bf.html 曙光服务器的IPMI http://noob.blog.1 ...