壹、面试复习之OC篇(二)

壹、面试复习之OC篇(一)



13.内存管理

  每个OC对象里都有一个引用计数器,用来统计正在被引用多少次,每个引用计数器占4个字节,对象刚刚创建时引用计数器默认为1。如果OC对象引用计数器为0时,系统就可以回收这个对象了。

 引用计数器的操作:

  (1).给对象发送一条retain消息,可以使引用计数器值+1(retain方法返回对象本身)

  (2).给对象发送一条release消息,可以使引用计数器-1

  (3).给对象发送一条retainCount消息,可以获得当前的引用计数器值

 对象的销毁:

  (1).当一个对象的引用计数器的值为0时,那么它将被销毁,其占用的内存会被系统回收

  (2).当一个对象被销毁时,系统会自动向对象发送一条dealloc消息

  (3).一般会重写dealloc方法,在这里释放相关资源,dealloc就像是对象的遗言

  (4).一旦重写了dealloc方法,就必须调用[super dealloc],并且放在最后面调用(ARC中不需要调用[super dealloc],因为系统会帮我们做)

  (5).不要直接调用dealloc方法,它只能重写

  (6).一旦对象被回收了,它占用的内存不再可用,坚持使用就会导致程序崩溃(因为已经是僵尸对象)

 概念:

  僵尸对象:所占用的内存已经被回收的对象,僵尸对象不能再使用

  野指针:指向僵尸对象的指针,给野指针发送消息会报错

  空指针:没有指向任何东西的指针(存储的东西是nil,null,0),给空指针发送消息不会报错(所以如果一个对象内存已经被释放的话,一定要将它的指针置为空指针,以免出现野指针)

  备注:nil和null不同,null是一个宏定义,值为0,nil表示无值,任何指针变量在没有赋值之前都是nil,对于真假判断,只有nil和false表示假,其他皆为真

 内存管理原则:

  (1).谁创建,谁release:如果你通过alloc、new或者[mutable]copy来创建一个对象,那么必须调用release或autorelease

  (2).谁retain,谁release: 只要你调用了retain,无论这个对象如何生成的,你都要调用release

14.setter方法内存管理

  Setter方法中,会对一个retain修饰的属性进行retain操作,那么,我们必须保证在对象销毁前,对这个属性进行release操作.

  比如一个人person有一个属性book:

- (void)dealloc{

    [_book release];//b引用计数器减1

    [super dealloc];//这个方法写在最后

}

- (void)test{

    Person *p = [Person new];//alloc,p引用计数器为1

    Book *b = [Book new];//alloc,b引用计数器为1

    p.book = b;//调用setter方法,b引用计数器为2

    [p release];//p应用计数器0,将被系统回收,系统会给p发送dealloc消息

    p = nil;//置为空指针,防止野指针出现

    /**

     由于调用setter方法时,p对b进行了引用计数器加1的操作,那么我们得在dealloc中将b的引用计数器减1

     */

}

- (void)setBook:(Book *)book{

    //判断是否传进来的是旧对象

    if (_book != book) {

        [_book release];//旧对象引用计数器减1

        _book = book.retain;//新对象引用计数器+1

    }

}

在工程中同时支持ARC与MRC,方法:

  在Build Phase里面的Compile Source中找到需要特殊处理的文件,双击加上编译选项(Complier Flags):

  (1)项目是ARC,文件需要支持MRC,那么加上”-fno-objc-arc”

  (2)项目是MRC,文件需要支持ARC,那么加上”-fobjc-arc”

如果对于代码中要分别处理ARC和MRC,可以加上条件编译这样写:

- (void)dealloc{

#if !__has_feature(objc_arc)//下方写MRC的处理

    [_book release];//b引用计数器减1

    [super dealloc];//这个方法写在最后

#else

    /**这里写ARC时候的处理 */

#endif

}

15.@class

@class是对一个类的声明,它仅仅是告诉编译器有这么一个类。它主要用于在一个类的.h文件中声明另一个类,防止两个类同时在.h文件中#import对方造成的循环拷贝.

在实际开发中,使用如下:

  (1).在.h文件中用@class来声明类

  (2).在.m文件中#import类

16.两个类的循环引用

 在实际开发中,两个类中会出现都要声明对方为自己的属性的情况,如果都用strong/retain,那么会出现”你包含我,我包含你”问题,在对对象发送release消息时,会出现占用内存永远回收不了的问题。解决方法:

-个修饰符用strong/retain,一个修饰符用weak

17.autorelease(自动释放池)

自动释放池是通过以AutoreleasePoolPage为节点的双向链表来实现的。它的行为和栈类似(先进后出)

autorelease的基本用法:

  (1).会将对象放到一个自动释放池中

  (2).当自动释放池被销毁时,会对池子里面的所有对象做一次release操作

  (3).会返回对象本身

  (4).调用完autorelease方法后,对象的引用计数器值不变

  在ios5.0之前,autorelease写法为

    NSAutoreleasePool *autoreleasePool = [[NSAutoreleasePool alloc]init];

    //这里会将该对象加入最新创建的自动释放池里面

    Person *p = [[Person new]autorelease];

    [autoreleasePool release];

  在ios5.0之后

    @autoreleasepool

    {//此处创建自动释放池

        Person *p = [[Person new]autorelease];

    }//此处销毁自动释放池

autorelease好处:

  (1).不用再关心对象释放的时间

  (2).不用再关心什么时候调用release

autorelease使用注意:

  (1).占用内存交大的对象不要随便使用autorelease

  (2).占用内存较小的对象使用autorelease,没有太大影响

autorelease错误用法

  (1).alloc之后调用了autorelease,又调用release

    @autoreleasepool

    {

        Person *p = [[Person new]autorelease];

        [p release];//错误,它会自动释放

    }

  (2).连续调用多次autorelease

    @autoreleasepool

    {

        Person *p = [[[Person new]autorelease]autorelease];//错误,只能调用一次autorelease

    }

自动释放池

  (1).在ios程序运行过程中,会创建无数个池子。这些池子都是以栈结构存在(先进后出)

  (2).当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池

备注:系统自带的方法中,如果不包含alloc、new、copy,那么这些方法返回的对象都是已经autorelease过的,比如:

    [NSString stringWithFormat:@""];

    [NSDate date];

  开发中经常写一些类方法快速创建一个autorelease对象(创建对象的时候不要直接使用类名,使用self),例如:

//以后可以只用使用这个类方法类创建对象

+ (instancetype)person{

    return [[[self alloc]init]autorelease];

}

18.ARC(内存回收)

ARC是编译器特性,可以理解为XCode的功能,它主要用来帮助用户做内存管理工作。

ARC判断准则:

  只有没有强指针指向对象,就会释放对象。同时系统还会根据弱指针的情况及时释放弱指针对象,避免野指针的产生

指针分两种:

  (1).强指针:默认情况下,所有指针都是强指针(__strong,属性修饰符为strong)

  (2).弱指针:定义一个指针是弱指针,只需要在定义钱加上__weak声明即可(属性修饰符为weak)

ARC特点:

  (1).不允许调用release、retain、retainCount

  (2).允许重写dealloc,但是不允许调用[super dealloc]

  (3).@property的参数

      ①strong:成员变量是强指针(适用于OC对象类型)

      ②weak:成员变量是弱指针(适用于OC对象类型)

      ③assign:适用于非OC对象类型

  (4).以前的retain改为用strong

19.Block

定义:

返回值类型 (^block名称)(参数表) =

    ^(参数表){

      //代码块部分

    };

    int (^sumBlock)(int,int) = ^(int a,int b){

        return a+b;

    };

    int sum = sumBlock(10,2);

Block和函数很像,Block特点:

  (1).可以保存代码

  (2).可以有返回值

  (3).可以有形式参数

  (4).调用方式和调用函数一样

  (5).可以像方法一样调用外面的变量

  (6).默认条件下,Block不能修改外面的局部变量,但是在变量名签名加上__block修饰就可以使Block代码块中修改这个变量。(Block默认不能修改局部变量,但是Block可以修改全局变量)

  (7).实际开发中,常用typedef定义Block以减少代码量

typedef int (^MyBlock) (int,int);

@property (nonatomic,copy) MyBlock block;

    //调用

    self.block = ^int(int a, int b) {

        return a % b;

    };

备注:当block要用到外卖对象的指针的时候,要把这个指针使用__weak或者__unsafe_unretained声明为弱指针,否则会出现循环引用的问题,声明方法:

    //使用__weak声明,不能用在MRC中,否则会报错

    __weak typeof(self) weakSelf = self;

    //使用__unsafe_unretained声明

    __unsafe_unretained typeof(self) unRetainedSelf = self;

后面在Block代码块中调用就使用弱指针,就不会出现循环引用了

20.block底层原理

关于Block底层实现原理可以看看王威大神的这篇文章:Block技巧与底层解析

大致的信息如下:

Block的底层结构为:

/* Revised new layout. */

struct Block_descriptor{

    unsigned long int reserved;

    unsigned long int size;

    void (*copy)(void *dst, void *src);

    void (*dispose)(void *);

};

struct Block_layout {

    void *isa; int flags;

    int reserved;

    void (*invoke)(void *, ...);

    struct Block_descriptor *descriptor;

    /* Imported variables. */

};

  (1).block底层实现为一个一个结构体,其中有isa指针,所以block也是一个对象(runtime里面,对象和类都是用结构体表示,并且都有isa指针)

  (2).invoke为block执行时调用的函数指针,记录函数地址,block定义时内部的执行代码都在它指向的函数中

  (3).flags为标志变量,在实现block内部操作时会用到;reserved为保留变量

  (4).Block_descriptor是对block的详细描述

      .copy/dispose,辅助拷贝/销毁函数,处理block范围外的变量时使用

总结:block就是一个里面存储了指向函数体中包含定义block时的代码块的函数指针,以及block外部上下文变量等信息的结构体

Block的类型:(类型看的是isa指针指向哪种)

  (1)._NSConcreteGlobalBlock  全局,创建的位置为全局区

  (2)._NSConcreteStackBlock  栈  创建的位置在函数体内

  (3)._NSConcreteMallocBlock  堆  这种不能直接创建,只能由栈类的block拷贝而来,他会调用_Block_copy_internal函数类拷贝,这个函数的实现中使用memove将栈中的block的内存拷贝到堆中,并将新拷贝过来的block的isa指针指向_NSConcreteMallocBlock

捕捉变量对block结构的影响:

局部变量

    int a = 5;

    __block typeof(a) blockA = a;

    ^{blockA = 10;};

  如果直接将a传入block,不能修改,原因是block的实现中,定义了一个和a同类型的成员变量来存储外部变量a的值,这次的拷贝是一个值传递,因为作用域不同,所以直接对a赋值是没有意义的,所以编译器给了错误。所以我们要使用__blcok修饰局部变量

全局变量

  由于全局变量都是在静态数据存储区,在程序结束前不会被销毁,所以block直接访问到了对应的变量,没有创建中间变量,所以全局变量可以直接在block中进行赋值等修改操作

局部静态变量

  静态变量和全局变量一样,都是存储在静态数据存储区,和程序拥有一样的生命周期,也就是说在程序运行时,都能够保证block访问到一个有效的变量。但是要注意的是,局部静态变量的作用域还是在定义它的函数中,所以只能在block通过静态局部变量的地址来进行访问

__block修饰的变量

  使用__block修饰后,在实现中会创建一个结构体b来包装局部变量a,在block被拷贝到堆中时,拷贝辅助函数会将这个结构体拷贝到堆中,堆中结构体b的__forwarding指针指向自身,栈中的__forwarding指针指向堆中的拷贝,这样就可以保证操作的值始终是堆中的拷贝,而不是栈中的值,这样也就可以对局部变量进行修改了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • 面试题参考1 : 面试题[http://www.cocoachina.com/ios/20150803/12872...
    江河_ios阅读 1,626评论 0 4
  • OC基础总结 重新回过头看这些基础知识,对许多知识点都有新的认识,拥有坚实的基础才能更快的成长。 OC内存管理 -...
    xx_cc阅读 2,646评论 1 21
  • 内存管理的基本范围和概念. 程序运行过程中药创建大量的对象, 和其他高级语言类似,在ObjC中对象存储在堆区,程序...
    ValienZh阅读 822评论 0 2
  • 内存管理 简述OC中内存管理机制。与retain配对使用的方法是dealloc还是release,为什么?需要与a...
    丶逐渐阅读 1,867评论 1 16
  • 她渐渐发现自己正逡巡于停滞的车流中。 是身体带动着她走。仿佛走了很久,但她感觉不到脚的酸胀,脚的疼痛,脚步的声音,...
    海霖阅读 414评论 0 0