内存管理
代码1
栈-堆-数据段-只读数据段-代码段
地址由高到低
#include
#include
void foo {
}
int a = 10;
// malloc realloc calloc 分配内存
int main(void){
char str1[20] = "Gello, world!";
char *str2 = "Hello, world!";
// str2[0] = 'G'; 修改不了
char *str3 = (char *)malloc(1000);
printf("栈(stack): str1 = %p\n", str1);
printf("堆(heap): str3 = %p\n", str3);
printf("数据段:a = %p\n", &a);
printf("只读数据段 str2 = %p\n", str2);
printf("代码段: foo = %p\n", foo);
return 0;
}
代码2
任何时候希望通过函数调用修改传入的参数
那就不能只传入参数的值 而要传参数的地址
如果传入的参数本身是指针,那么就要使用指向指针的指针
#include
#include
#include
// 指针的第一个用途就是实现跨栈操作
void get_memory(char **p, int m){
// 指针的第二个用途就是申请堆空间
*p = malloc(sizeof **p * m);
}
int main(){
char *str = NULL;
get_memory(&str, 100);
// 判断空间申请是否成功
if(str){
strcpy(str, "Hello, world!");
printf("%s\n", str);
// 堆空间不会随着栈的消失而消失 需要手动释放
free(str);
str = NULL; // 经验,记住释放空间后要写上
}
return 0;
}
代码3
一个函数可以返回栈空间的数据,但是不能返回栈空间的地址
#include
char * get_memory(){
// 一个函数可以返回栈空间的数据,但是不能返回栈空间的地址
//char str[] = "Hello, world!";
char *str = "Hello, world!";
return str;
}
int main(){
char *str = get_memory();
if(str != NULL){
printf("%s\n", str);
// 不能释放非堆空间
// free操作跟malloc操作是成对出现的
// free(str);
}
return 0;
}
代码4
申请内存后一定要判断后再使用
用完内存一定要释放 否则有内存泄露的风险
#include
#include
#include
char * get_memory (int m) {
char *str = malloc(sizeof *str * m);
return str;
}
int main() {
char *str = get_memory(100);
// 申请内存后一定要判断后再使用
if(str) {
strcpy(str, "Hello, world!");
printf("%s\n", str);
// 用完内存一定要释放 否则有内存泄露的风险
free(str);
str = NULL; // very important
}
}
ARC:自动内存管理
MRC:手动内存管理
不要再C的结构体中使用对象指针,因为无法进行内存管理
坚持使用ARC 不要使用MRC
如果非要用MRC 记住:谁创建谁释放 谁加1谁减1
自动释放池是可以嵌套的
ARC 模式下要正确的使用内存最关键的就是书写正确的和内存管理相关的属性修饰符
strong
- 对象指针一般都用 strong 表示对引用计数+1(默认值)
weak
- 1.对象出现循环引用的时候必须使用 有一方必须使用 weak(破除循环引用)
- 2.一个对象的生命周期不由你自己的代码管理
- 3.属性是一个协议指针,也应该使用weak
copy
- 1.确保 NSString、NSArray、NSDictionary、NSData等不可变类型的指针确确实实的指向一个不可变对象
- 2.如果属性是一个Block类型的变量必须 copy
assign
- 1.非对象指针类型(整型、字符型、实型、布尔型、枚举、结构体、联合体)都用assign
- 2.如果属性桑对象指针 assign 相当于 weak
EXC_BAD_ACCESS(异常的坏的访问)
- x-code报这个错误的时候,是使用了野指针,访问了不能访问的内存,该内存已经被释放了
要使用手动内存管理 设置方法
个别文件使用手动内存管理 的设置方法
内存管理复习
//问:你说说对内存管理的理解?(说原理:引用计数)
// 手动(MRC)原理:1.在创建一个对象的时候系统会自动创建这个对象的引用计数,并且赋值为1;
// 2.当引用计数为0的时候,对象会去调用dealloc方法,来销毁对象;
// 3.对象调用release方法会让引用计数减1,调用retain方法让对象的引用计数加1;
// 自动(ARC):在ARC管理内存的实质还是通过引用计数器去管理的,但是程序员不再去关心引用计数器的值。在ARC环境下,系统会在程序编译的时候会自动的在合适的地方添加retain、release或者autorelease。
// 当有强指针指向对象的时候,对象不销毁;弱指针不影响对象的销毁;指针默认都是强指针
// 手动内存管理的原则:程序中如果出现alloc、retain、new必须配对出现一个release或者autorelease;谁创建谁释放,在哪创建在哪释放。
//手动使用的
// autoReleasePool的原理和autorelease的作用
// autoReleasePool的原理:当autoReleasePool销毁的时候,会将自动释放池所有的对象调用一次release方法
// autorelease的作用:将对象放到自动释放池中(并不是写在自动释放池大括号中的对象就是在自动释放池中的对象)
// 对象需要延迟销毁的时候,可以使用autorelease,
// MRC 中符合内存管理的setter方法的书写(旧值release,新值retain,然后赋值)
// 属性修饰符(strong weak copy assign retain)
// strong 控制@property实现符合内存管理的setter方法,引用计数加1;修饰一般的对象
// weak 控制@property实现符合一般的setter方法(直接赋值的方法),修饰对象用来避免循环引用(最常用的delegate)
// copy 控制@property实现的setter方法,会先创建一个新的对象,将参数的值传给新的对象,最后将新的对象赋值给成员变量。常用来修饰字符串、数组、字典、和block、NSData
// assign 控制@property实现符合一般的setter方法(直接赋值的方法) 常用来修饰基本数据类型、结构体、枚举、布尔型、联合体
// retain:在MRC中相当于strong(实现的setter方法 旧值release,新值retain,然后赋值) 使用的时候要避免循环引用
// 内存管理的作用:解决内存泄露和野指针操作
// 内存管理需要注意什么: 解决内存泄露和野指针操作
代码1
#import
#import "ZPStudent.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 1.创建了一个学生对象(堆上)
// 2.创建了一个指针(栈上)
// 3.指针指向了学生对象(指针中存储了学生对象的地址)
// 使用__weak这个关键字修饰的指针式弱指针。
__weak ZPStudent *stu = [[ZPStudent alloc] init];
}
return 0;
}
代码2
#import
#import "ZPStudent.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// retainCount = 1;
ZPStudent *stu = [[[ZPStudent alloc] init] autorelease];
// autorelease 将对象添加到自动释放池中
// autorelease 这个必须在自动释放池的作用域调用
}//括号结束的时候,在这儿自动释放池会被销毁,会调用自动释放池中所有对象的release方法
return 0;
}