【iOS开发-33】学习手动内存管理暂时抛弃ARC以及retain/assign知识——iOS工程师面试必考内容

我们为什么需要内存管理?当使用内存达到40M和45M时候会发出警告,如果不处理,占用内存达到120M时直接强制关闭程序。所以出现闪退除了是程序出现逻辑错误,还有可能是内存使用过大。

(1)创建一个对象的过程:先分配内存空间存储对象;初始化成员变量;返回对象的指针。

(2)对象在创建时,内部会自动创建一个引用计数器retainCount,当retainCount=0时,系统会回收当前对象,retainCount是唯一判断标记。release会-1,retain会+1,retain后返回的是自己self指针。

(3)将ARC关闭调成手动内存管理模式。

(4)内存配对原则:只要有new、alloc或retain,都需要配对一个release或autorelease。

在main.m中,需要把@autorelease pool{}去除,完全靠手动管理:

#import <Foundation/Foundation.h>
#import "Person.h"

int main(int argc, const char * argv[]) {

        //alloc时retainCount=1
        Person *p1=[[Person alloc]init];
        NSLog(@"%lu",p1.retainCount);

        //引用计数=2
        [p1 retain];
        NSLog(@"%lu",p1.retainCount);

        //引用计数=1
        [p1 release];
        NSLog(@"%lu",p1.retainCount);

        //引用计数=0
        [p1 release];
        //怎么验证此时对象已被销毁呢?每个对象被销毁时会调用dealloc方法,我们重写dealloc,让它输出一些东西,证明被调用了即可
        //重写是在这个对象所属类的.m文件中,即本例的Person.m中

    return 0;
}

在Person.m中:

#import "Person.h"

@implementation Person

-(void)dealloc{

    //在对象被销毁之前,先用以下语句释放父类中得相关对象
    [super dealloc];
    NSLog(@"i am dealloc");
}

@end

(5)一个对象被回收后,这个对象被称之为僵尸对象,因为Xcode不会时时检查僵尸对象,所以在CMD+R运行时,访问已经被回收的对象有时会报错有时不报错。如果想Xcode时时检查,以下设置。默认是不时时检查,是为了提高编码效率。但建议关闭,因为当类比较多项目大时很耗内存。

(6)手动内存管理研究的两个问题就是:野指针和内存泄露。当对象对回收后,该指针变量没有被设置为nil时,这个&#28;指针就是野指针,会导致程序出现闪退。所以我们一般在release后把这个指针变量设置为nil。尽管后续还用到p1,但是p1是nil,向其发送任何消息都没回应也没影响,不会出现闪退。

(7)内存泄露:第一种情况是,不再被使用的对象没有回收一直存在内存中。第二种情况是,不小心先把指针变量设置为nil,然后再[p1 release];已经无效了,此时因为retainCount是唯一标记,尽管没有指针指向这个对象,但对象依然存在在内存中。

#import <Foundation/Foundation.h>
#import "Person.h"

int main(int argc, const char * argv[]) {

        //alloc时retainCount=1
        Person *p1=[[Person alloc]init];
        NSLog(@"%lu",p1.retainCount);

        //引用计数=2
        [p1 retain];
        NSLog(@"%lu",p1.retainCount);

        //引用计数=1
        [p1 release];
        NSLog(@"%lu",p1.retainCount);

        //引用计数=0
        [p1 release];
        //怎么验证此时对象已被销毁呢?每个对象被销毁时会调用dealloc方法,我们重写dealloc,让它输出一些东西,证明被调用了即可
        //重写是在这个对象所属类的.m文件中,即本例的Person.m中

    return 0;
}

(8)定义一个p1指针变量后,将其赋值给另一个指针变量p2,那么这个对象retainCount不变,只是多了一个p2指向这个对象而已。任何一个p1和p2都可以release这个对象,release后,这两个指针变量如果不设置为nil就变成野指针。

(9)将指针变量作为参数传递给其他方法时,不会增加其指向对象的引用计数。所以归根结底还是看是否有无retain、new、alloc,以及看是否与release、autorelease配对使用。

(10)Car类是Person类的子类,Person类有个方法是-(void)setCar:(Car *)car{_car=car;}和-(void)drive{[_car run];},run方法是Car类的方法。在主函数中我们的Person对象p1在alloc时候需要配对一个release。如果需要使用[p1 drive];那么需要实例化一个Car对象car1,那么也需要为car1匹配个release。一般是car1的alloc和release被包裹在p1的alloc和release里面。如果我计划在[car1
release];和[p1 release];之间再使用p1的任意方法,比如[p1 drive];发现因为car1已经被release,所以会报错。也就是说,我的p1对象还存在,但是我已经不能任意使用它所有的方法了。怎么解决?

第一步:在setCar方法中修改为_car=[car retain];相当于给这个car1对象的retainCount+1,这样就算我们在主函数中使用配对原则,retainCount仍然还有1,这样p1就可以任意使用。

第二步:就是消除上面仍然存在的car1的retainCount的1,我们的目的是要这个car1的retainCount一直是1,除非我的p1不在了,那car1可以不在。所以我们需要在p1不在的时候把它消除,即在p1的dealloc里面把它消除,消除就是把它[_car release];

(11)接以上问题:如果在主函数里面连续定义两个Car对象变量car1和car2,则最后一个对象变量的内存管理正常,但是前面的几个不正常。因为car1的retainCount还有1,在dealloc里面的_car是car2,所以car2内存正常,car1不正常,内存泄露。解决办法?

问题还是集中在setCar中,在前面增加一句[_car release];如此便好。当第一次调用_car是nil,不影响,转了一圈出去后car1的retainCount为2,当第二次调用时[_car release];里的_car是car1,所以把car1释放了,然后接着把car2的retainCount变成2,随后在主函数以及dealloc中分别释放掉car2。

(12)接以上问题,如果我第二次调用setCar时不时传递car2,而是依然传递的时car1,那么此时在第二次调用时,遇到[_car release];则已经释放了car1,所以下面一句_car=[car retain];就变成野指针操作了。解决办法?

我们给这个[_car release};增加1个判断语句。因为如果是同一个对象没有新对象传递进来的话,_car就一直是这个对象,那么我们会在dealloc中的[_car release];释放掉,而如果后来传递的对象和上一次的不同的话,在dealloc中得[_car release];会变成第二个对象在释放,第一个对象就少了一个释放操作。所以我们的结论是,判断一下,如果传递的新对象是上一次的那个,就不在这个释放,否则就在这里释放。即变成if(_car!=car){[_car release];_car=[car
retain];},这句话的核心就是release旧值,retain新值。而且如果传递的和上次一样的对象,则不做任何操作,retainCount仍然是1。

(13)所以,总结成员变量对象的内存管理,就是:a)主要针对setter方法;b)在setter方法中增加一个if(新值!=旧值){release旧值,retain新值};c)并在类的dealloc函数中写一个release。

(14)其实利用@property定义成员变量的时候其实不同的参数就是在指示程序生成不同类型的setter方法。retain是指示程序生成符合内存管理的setter方法,就是上面的if的那一串。而assign则是普通setter方法,所以我们一般对对象类型用retain,而基本数据类型用assign。

但是,关键是上面的retain只是改变了setter方法,并没有自动在dealloc中release。所以,我们在用@property(retain)***的时候,虽然不需要重写setter方法,但是需要重写类的dealloc方法来release释放一次这个成员变量。

(15)顺便,线程管理参数atomic是mac开发的残留物,一般iOS都是nonatomic,而且要书写,因为默认是atomic,所以一般只有两种形式@property(nonatomic,retain)Char *char1;和@property(nonatomic,assign) int age;

(16)顺便,readwrite(生成getter和setter方法)和readonly(只生成getter方法),默认是readwrite。可以利用以下语句更换默认的setter或者getter方法名@property(nonatomic,assign,setter=abc) int age;以后调用setter方法就直接用abc代替。这个一般用在定义BOOL变量时改变setter和getter的名称,一般改成isXXX。

时间: 2024-12-22 01:08:39

【iOS开发-33】学习手动内存管理暂时抛弃ARC以及retain/assign知识——iOS工程师面试必考内容的相关文章

【iOS开发-33】学习手动内存管理临时抛弃ARC以及retain/assign知识——iOSproject师面试必考内容

我们为什么须要内存管理?当使用内存达到40M和45M时候会发出警告,假设不处理,占用内存达到120M时直接强制关闭程序. 所以出现闪退除了是程序出现逻辑错误,还有可能是内存使用过大. (1)创建一个对象的过程:先分配内存空间存储对象:初始化成员变量:返回对象的指针. (2)对象在创建时,内部会自己主动创建一个引用计数器retainCount,当retainCount=0时,系统会回收当前对象,retainCount是唯一推断标记.release会-1.retain会+1,retain后返回的是自

iOS-旧项目中手动内存管理(MRC)转ARC

在ARC之前,iOS内存管理无论对资深级还是菜鸟级开发者来说都是一件很头疼的事.我参 加过几个使用手动内存管理的项目,印象最深刻的是一个地图类应用,由于应用本身就非常耗内存,当时为了解决内存泄露问题,每周都安排有人值班用 Instruments挨个跑功能,关键是每次都总能检查出来不少.其实不管是菜鸟级还是资深级开发者都避免不了写出内存泄露的代码,规则大家都懂,可是 天知道什么时候手一抖就少写了个release? 好在项目决定转成ARC了,下面将自己转换的过程和中间遇到的问题写出来和大家共享,希望

IOS开发系列—Objective-C之内存管理

概述 我们知道在程序运行过程中要创建大量的对象,和其他高级语言类似,在ObjC中对象时存储在堆中的,系统并不会自动释放堆中的内存(注意基本类型是由系统自己管理的,放在栈上).如果一个对象创建并使用后没有得到及时释放那么就会占用大量内存.其他高级语言如C#.Java都是通过垃圾回收来(GC)解决这个问题的,但在OjbC中并没有类似的垃圾回收机制,因此它的内存管理就需要由开发人员手动维护.今天将着重介绍ObjC内存管理: 引用计数器 属性参数 自动释放池 引用计数器 在Xcode4.2及之后的版本中

iOS开发系列—Objective-C之内存管理(前辈写的,借鉴了一下,超赞)

概述 我们知道在程序运行过程中要创建大量的对象,和其他高级语言类似,在ObjC中对象时存储在堆中的,系统并不会自动释放堆中的内存(注意基本类型是由系统自己管理的,放在栈上).如果一个对象创建并使用后没有得到及时释放那么就会占用大量内存.其他高级语言如C#.Java都是通过垃圾回收来(GC)解决这个问题的,但在OjbC中并没有类似的垃圾回收机制,因此它的内存管理就需要由开发人员手动维护.今天将着重介绍ObjC内存管理: 引用计数器 属性参数 自动释放池 引用计数器 在Xcode4.2及之后的版本中

【iOS开发-34】自己主动释放池@autoreleasepool的使用注意事项以及ARC机制——面试必考内容

自己主动释放池@autorelease面试频率可能会吧release还要高. (1)在自己主动释放池@autoreleasepool{}中alloc一个对象后(如p1).仍然须要用[p1 autorelease];不过这个语句和[p1 release];不同.后者表示把p1的retainCount-1,而前者只表示把p1放到自己主动释放池中返回一个self,自己主动释放池结束销毁时,统一对里面的对象引用计数retainCount-1. (2)@autoreleasepool{}能够任意创建.也能

【iOS开发-34】自动释放池@autoreleasepool的使用注意事项以及ARC机制——面试必考内容

自动释放池@autorelease面试频率可能会吧release还要高. (1)在自动释放池@autoreleasepool{}中alloc一个对象后(如p1),仍然需要用[p1 autorelease];只是这个语句和[p1 release];不同,后者表示把p1的retainCount-1,而前者仅仅表示把p1放到自动释放池中返回一个self,自动释放池结束销毁时,统一对里面的对象引用计数retainCount-1. (2)@autoreleasepool{}可以随意创建,也可以嵌套使用.

iOS开发进阶(iOS开发实战:理解内存管理)summary

1.引用计数 什么是引用计数. 引用计数可以有效的管理对象生命周期,它可以帮我们记录一个对象有多少指针指向,进行很好的内存的管理!! 例子: 假设在一个办公室里面只有一个照明设备,第一个上班进入的人把灯打开,然后持续的进来咯两个人,如果我们没有记住现在在办公室有多少人,而第一个人离开的时候把灯直接关掉,会导致另外两个人没有照明,进行不了工作!! 所以引用计数就是把这些人都记录下来,办公室现在究竟还有多少人,如果有人在的时候依然照明,一直直到没有人的时候才把灯关掉!!这样可以当有人时候没有照明的问

小白学开发(iOS)OC_手动内存管理(2015-08-02)

// //  main.m //  手动内存管理 // //  Created by admin on 15/8/3. //  Copyright (c) 2015年 admin. All rights reserved. // #import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { /* 当我们创建一个对象时: 1. 分配内存空间,存储对象

IOS基础 Day-1手动内存管理

辞职回家打算自学IOS开发,就在借个地方记录一下 Day-1      手动内存管理                   主要内容:release  retain必须配对好,不然会占用内存 慢慢积累导致错误 一旦内存占用超过40m 45m时分别发生警告,一旦超过120m 系统将kill你的app 发生闪退 主要要防止发生的问题: 1.野指针操作 2.内存泄漏 理解retain和assign的区别和 retain的原理 Main 1 // 2 // main.m 3 // 1-1内存管理 4 //