內(nèi)存管理
代碼1
棧-堆-數(shù)據(jù)段-只讀數(shù)據(jù)段-代碼段
地址由高到低
#include<stdio.h>
#include<stdlib.h>
void foo {
}
int a = 10;
// malloc realloc calloc 分配內(nèi)存
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("數(shù)據(jù)段:a = %p\n", &a);
printf("只讀數(shù)據(jù)段 str2 = %p\n", str2);
printf("代碼段: foo = %p\n", foo);
return 0;
}
代碼2
任何時(shí)候希望通過(guò)函數(shù)調(diào)用修改傳入的參數(shù)
那就不能只傳入?yún)?shù)的值 而要傳參數(shù)的地址
如果傳入的參數(shù)本身是指針拐迁,那么就要使用指向指針的指針
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
// 指針的第一個(gè)用途就是實(shí)現(xiàn)跨棧操作
void get_memory(char **p, int m){
// 指針的第二個(gè)用途就是申請(qǐng)堆空間
*p = malloc(sizeof **p * m);
}
int main(){
char *str = NULL;
get_memory(&str, 100);
// 判斷空間申請(qǐng)是否成功
if(str){
strcpy(str, "Hello, world!");
printf("%s\n", str);
// 堆空間不會(huì)隨著棧的消失而消失 需要手動(dòng)釋放
free(str);
str = NULL; // 經(jīng)驗(yàn)线召,記住釋放空間后要寫上
}
return 0;
}
代碼3
一個(gè)函數(shù)可以返回椩钏眩空間的數(shù)據(jù)割卖,但是不能返回椈汲空間的地址
#include<stdio.h>
char * get_memory(){
// 一個(gè)函數(shù)可以返回椦吐兀空間的數(shù)據(jù)匀借,但是不能返回椣爬撸空間的地址
//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操作是成對(duì)出現(xiàn)的
// free(str);
}
return 0;
}
代碼4
申請(qǐng)內(nèi)存后一定要判斷后再使用
用完內(nèi)存一定要釋放 否則有內(nèi)存泄露的風(fēng)險(xiǎn)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
char * get_memory (int m) {
char *str = malloc(sizeof *str * m);
return str;
}
int main() {
char *str = get_memory(100);
// 申請(qǐng)內(nèi)存后一定要判斷后再使用
if(str) {
strcpy(str, "Hello, world!");
printf("%s\n", str);
// 用完內(nèi)存一定要釋放 否則有內(nèi)存泄露的風(fēng)險(xiǎn)
free(str);
str = NULL; // very important
}
}
ARC:自動(dòng)內(nèi)存管理
MRC:手動(dòng)內(nèi)存管理
不要再C的結(jié)構(gòu)體中使用對(duì)象指針肤舞,因?yàn)闊o(wú)法進(jìn)行內(nèi)存管理
堅(jiān)持使用ARC 不要使用MRC
如果非要用MRC 記住:誰(shuí)創(chuàng)建誰(shuí)釋放 誰(shuí)加1誰(shuí)減1
自動(dòng)釋放池是可以嵌套的
ARC 模式下要正確的使用內(nèi)存最關(guān)鍵的就是書寫正確的和內(nèi)存管理相關(guān)的屬性修飾符
strong
- 對(duì)象指針一般都用 strong 表示對(duì)引用計(jì)數(shù)+1(默認(rèn)值)
weak
- 1.對(duì)象出現(xiàn)循環(huán)引用的時(shí)候必須使用 有一方必須使用 weak(破除循環(huán)引用)
- 2.一個(gè)對(duì)象的生命周期不由你自己的代碼管理
- 3.屬性是一個(gè)協(xié)議指針芒率,也應(yīng)該使用weak
copy
- 1.確保 NSString、NSArray德玫、NSDictionary、NSData等不可變類型的指針確確實(shí)實(shí)的指向一個(gè)不可變對(duì)象
- 2.如果屬性是一個(gè)Block類型的變量必須 copy
assign
- 1.非對(duì)象指針類型(整型萄窜、字符型查刻、實(shí)型穗泵、布爾型谜疤、枚舉夷磕、結(jié)構(gòu)體坐桩、聯(lián)合體)都用assign
- 2.如果屬性桑對(duì)象指針 assign 相當(dāng)于 weak
EXC_BAD_ACCESS(異常的壞的訪問(wèn))
- x-code報(bào)這個(gè)錯(cuò)誤的時(shí)候绵跷,是使用了野指針碾局,訪問(wèn)了不能訪問(wèn)的內(nèi)存净当,該內(nèi)存已經(jīng)被釋放了
要使用手動(dòng)內(nèi)存管理 設(shè)置方法
個(gè)別文件使用手動(dòng)內(nèi)存管理 的設(shè)置方法
內(nèi)存管理復(fù)習(xí)
//問(wèn):你說(shuō)說(shuō)對(duì)內(nèi)存管理的理解品擎?(說(shuō)原理:引用計(jì)數(shù))
// 手動(dòng)(MRC)原理:1.在創(chuàng)建一個(gè)對(duì)象的時(shí)候系統(tǒng)會(huì)自動(dòng)創(chuàng)建這個(gè)對(duì)象的引用計(jì)數(shù)萄传,并且賦值為1秀菱;
// 2.當(dāng)引用計(jì)數(shù)為0的時(shí)候,對(duì)象會(huì)去調(diào)用dealloc方法蹭睡,來(lái)銷毀對(duì)象衍菱;
// 3.對(duì)象調(diào)用release方法會(huì)讓引用計(jì)數(shù)減1,調(diào)用retain方法讓對(duì)象的引用計(jì)數(shù)加1肩豁;
// 自動(dòng)(ARC):在ARC管理內(nèi)存的實(shí)質(zhì)還是通過(guò)引用計(jì)數(shù)器去管理的脊串,但是程序員不再去關(guān)心引用計(jì)數(shù)器的值。在ARC環(huán)境下清钥,系統(tǒng)會(huì)在程序編譯的時(shí)候會(huì)自動(dòng)的在合適的地方添加retain琼锋、release或者autorelease。
// 當(dāng)有強(qiáng)指針指向?qū)ο蟮臅r(shí)候祟昭,對(duì)象不銷毀缕坎;弱指針不影響對(duì)象的銷毀;指針默認(rèn)都是強(qiáng)指針
// 手動(dòng)內(nèi)存管理的原則:程序中如果出現(xiàn)alloc谜叹、retain、new必須配對(duì)出現(xiàn)一個(gè)release或者autorelease;誰(shuí)創(chuàng)建誰(shuí)釋放,在哪創(chuàng)建在哪釋放。
//手動(dòng)使用的
// autoReleasePool的原理和autorelease的作用
// autoReleasePool的原理:當(dāng)autoReleasePool銷毀的時(shí)候勿璃,會(huì)將自動(dòng)釋放池所有的對(duì)象調(diào)用一次release方法
// autorelease的作用:將對(duì)象放到自動(dòng)釋放池中(并不是寫在自動(dòng)釋放池大括號(hào)中的對(duì)象就是在自動(dòng)釋放池中的對(duì)象)
// 對(duì)象需要延遲銷毀的時(shí)候莲组,可以使用autorelease,
// MRC 中符合內(nèi)存管理的setter方法的書寫(舊值release,新值retain,然后賦值)
// 屬性修飾符(strong weak copy assign retain)
// strong 控制@property實(shí)現(xiàn)符合內(nèi)存管理的setter方法,引用計(jì)數(shù)加1邪码;修飾一般的對(duì)象
// weak 控制@property實(shí)現(xiàn)符合一般的setter方法(直接賦值的方法)喻圃,修飾對(duì)象用來(lái)避免循環(huán)引用(最常用的delegate)
// copy 控制@property實(shí)現(xiàn)的setter方法杖小,會(huì)先創(chuàng)建一個(gè)新的對(duì)象,將參數(shù)的值傳給新的對(duì)象岗照,最后將新的對(duì)象賦值給成員變量躁劣。常用來(lái)修飾字符串熙宇、數(shù)組戳稽、字典荆几、和block诞吱、NSData
// assign 控制@property實(shí)現(xiàn)符合一般的setter方法(直接賦值的方法) 常用來(lái)修飾基本數(shù)據(jù)類型抬纸、結(jié)構(gòu)體、枚舉、布爾型命黔、聯(lián)合體
// retain:在MRC中相當(dāng)于strong(實(shí)現(xiàn)的setter方法 舊值release,新值retain,然后賦值) 使用的時(shí)候要避免循環(huán)引用
// 內(nèi)存管理的作用:解決內(nèi)存泄露和野指針操作
// 內(nèi)存管理需要注意什么: 解決內(nèi)存泄露和野指針操作
代碼1
#import <Foundation/Foundation.h>
#import "ZPStudent.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 1.創(chuàng)建了一個(gè)學(xué)生對(duì)象(堆上)
// 2.創(chuàng)建了一個(gè)指針(棧上)
// 3.指針指向了學(xué)生對(duì)象(指針中存儲(chǔ)了學(xué)生對(duì)象的地址)
// 使用__weak這個(gè)關(guān)鍵字修飾的指針式弱指針。
__weak ZPStudent *stu = [[ZPStudent alloc] init];
}
return 0;
}
代碼2
#import <Foundation/Foundation.h>
#import "ZPStudent.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// retainCount = 1;
ZPStudent *stu = [[[ZPStudent alloc] init] autorelease];
// autorelease 將對(duì)象添加到自動(dòng)釋放池中
// autorelease 這個(gè)必須在自動(dòng)釋放池的作用域調(diào)用
}//括號(hào)結(jié)束的時(shí)候刁标,在這兒自動(dòng)釋放池會(huì)被銷毀,會(huì)調(diào)用自動(dòng)釋放池中所有對(duì)象的release方法
return 0;
}