objective-c---SEL、内存管理(引用计数器、野指针、@property参数、循环retain、autorelease)

SEL

SEL的概念

在Objective-C中,SEL是选择器(selector)的一个类型。选择器就是指向方法的一个指针。
可以简单理解成:SEL其实是对方法的一种包装,将方法包装成一个SEL类型的数据。去找对应的方法地址。找到方法地址就可以调用方法。

//Person类中有 +test1方法和 -test2方法

Person *p = [Person new];
//调用对象方法 -test2 方法1
[p test2]

//方法2:利用sel间接调用
[p performSelector:@selector(test2)];

若调用的方法需要传参数

[p test3:@"chenfanfang"];

//或者用sel方法
[p performSelector:@selector(test3:)withObject:@"chenfanfang"];

//注意:方法若有参数,则写方法名不能漏了冒号:

创建SEL类型的数据

SEL s = @selector(test3);

根据字符串调用方法

//背景:已知有一个方法名叫 test2,根据方法名调用该方法

NSString *name = @"test2";
//将字符串转成SEL
SEL s = NSSelectorFromString(name);
//根据SEL调用方法
[p performSelector:s];

SEL总结

方法的存储位置:

  • 每个类的方法列表都存储在类对象中
  • 每个方法都有一个与之对应的SEL类型的对象
  • 根据一个SEL对象就可以找到方法的地址,进而调用方法
  • SEL类型的定义 typedef struct objc selector *SEL

SEL对象的创建

    SEL s = @selector(test);
    SEL s2 = NSSelectorFromString(@"test");

SEL对象的其他用法

    //将SEL对象转为NSString对象
    NSString *str = NSStringFromSelector(@selector(test));

    Person *p = [Person new];
    //调用对象的test方法
    [p performSelector:@selector(test)];

SEL的变量 _cmd

每个方法里面都有 _cmd这个SEL变量,指向当前方法(即代表当前方法)

- (void)print{
    NSString *str = NSStringFromSelector(_cmd);
    NSLog(@"%@",str);
}

调用方法

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

输出结果:
2015-03-23 21:47:53.400 oc知识点[6369:72252] print

_cmd使用注意死循环

注意:不能在方法中使用如下代码

[self performSelector:_cmd];
//会调用当前方法,从而产生死循环

内存管理(引用计数器、野指针、@property参数、循环retain、autorelease)

内存管理的范围

  • 管理任何继承了NSObject的对象。
  • 对其他基本数据类型(int ,char,float,double,struct,enum等)无效 。

对象的基本结构

  • 每个oc对象都有自己的引用计数器,是一个整数,表示对象被引用的次数,即有多少人正在使用这个oc对象
  • 应用计数器:4字节
    每个oc对象内部专门有4个字节的存储空间来存储引用计数器

引用计数器的作用

  • 当使用alloc、new或者copy创建一个新对象时,新对象的引用计数器默认就是1
  • 当一个对象的引用计数器值为0时,对象占用的内存就会被回收。换句话说:如果对象的计数器不为0,那么在整个程序运行过程,它占用的内存就不可能被回收,除非整个程序已经退出。

引用计数器的操作

  • 给对象发一条retain消息,可以使引用计数器值+1。retain方法返回对象本身。
  • 给对象发送一条release消息,可以使引用计数器值-1。
  • 可以给对象发送retainCount消息获得当前的引用计数器值。
    Person *p = [[Person alloc] init];
    //1、retain
    [p retain];  //retain方法返回的是对象本身
    //2、release
    [p release];
    //3、retainCount
    NSInteger c = [p retainCount];

对象的销毁

  • 当一个对象的引用计数器值为0时,那么它将被销毁,其占用的内存被系统回收
  • 当一个对象被销毁时,系统会自动向对象发送一条dealloc消息
  • 一般会重写dealloc方法,在这里释放相关资源,dealloc就像对象的遗言
  • 一旦重写了dealloc方法,就必须调用[super dealloc],并且放在代码的最后面调用
  • 不要直接调用dealloc方法
  • 一旦对象被回收了,它占用的内存就不再可用,坚持使用会导致程序崩溃(野指针错误)

野指针概念

指向僵尸对象(指向不可用内存)的指针。

    [p release];//若此刻计数器为0,对象p就被回收了,则下面的代码会报错
    [p retain];//发生野指针错误

预防野指针错误

    Person *p = [Person new];
    p = nil;    //指针为空指针,则下面运行没意义,不会报错
    [p release];
    [p release];
    [p release];

oc中不存在空指针错误(即给空指针发送消息,不报错)

内存管理代码规范:

  1. 只要调用了alloc,必须有release (autorelease)
  2. set方法代码规范
//情况1:基本数据类型,直接赋值

 1. (void)setAge:(int)age
{
    _age = age;
}
//情况2:oc对象类型

 2. (void)setCar:(Car *)car
{
    //1.先判断是不是新传进来的的对象
    if(car != _car){
        [_car release];
        _car = [car retain];
    }
}
  1. dealloc方法代码规范
    一定要调用[super dealloc],而且放到最后面
    对self(当前对象)拥有的其他对象做一次release
- (void)dealloc
{
    [_car release];
    [super dealloc];
}

@property参数(retain)与内存管理

  • 普通的@property Book *book ;只是简单的赋值,若想用@property给set方法赋值并且计数,只需加一个参数,如下:
@property (retain) Book *book;

//retain:在生成的set方法里面,release旧值,retain新值。
  • 注意:@property只影响set和get方法。- (void) dealloc 函数里关于内存管理的代码需要自己写。

@property里的参数

1、set方法内存管理相关的参数

  • retain :release旧值,retain新值(适用oc对象类型)
  • assign :直接赋值(默认),(适用于非oc对象类型)
  • copy :release旧值,copy新值

2、是否要生成set方法

  • readwrite :同时生成setter和getter的申明、实现
  • readonly :只会生成getter的申明、实现

3、多线程管理

  • nonatomic :性能高(一般用这个)
  • atomic :性能低(默认)

4、setter和getter方法的名称

@property (getter=方法名1,setter=方法名2:) int weitht;

//注:setter方法需要传入参数,故方法名2后面不能漏了冒号: 

setter和getter方法的名称的参数一般用于Bool类型的getter方法
例如:

@property (getter=isRich) BOOL rich;

//返回BOOL类型的方法名一般用is开头

循环retain(两端循环引用解决方案)

  • 一端用retain。一端用assign

autorelease

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // { 开始代表创建了释放池
        //autorelease方法会返回对象本身
        //当自动释放池被销毁时,会把池子里面的所有对象做一次release操作
        //调用完autorelease方法后,对象的计数器不变

        Person * p = [[[Person alloc] init] autorelease];
    }
}

autorelease的好处

  • 不用再关心对象释放的时间,即不再关心什么时候调用release

autorelease的使用注意

  1. 占用内存较大的对象不要随便使用autorelease(需要精确控制内存的释放时间)。
  2. 占用内存较小的对象使用autorelease。

autorelease常见错误

  • alloc之后调用了 autorelease,之后又调用了release
    @autoreleasepool {
        Person *p = [[[Person alloc] init] autorelease];
        [p release];
    }
  • 连续调用多次autorelease
    例如:
    @autoreleasepool {
        Person *p = [[[[Person alloc] init] autorelease] autorelease];

    }

自动释放池

  1. 在ios程序运行过程中,会创建无数个池子。这些池子都是以栈结构存在(先进后出)
  2. 当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池

autorelease可以写在类方法中

背景:

  Person *p = [[[Person alloc] init] autorelease];
  //这行代码太长,我们常在类中写一个类方法

//在Person类中,将autorelease写在类方法person中

+ (id)person
{
    return [[[Person alloc] init] autorelease];
    //最好把Person写成self
}



//在main中创建Person对象
Person *p = [Person person];




//若需要参数,可写成

+ (id) personWithAge:(int)age
{
    Person *p = [[[Person alloc] init] autorelease];
    p.age = age;
    return p;
}


//在main中创建Person对象
Person *p = [Person personWithAge:100];

有些对象默认是autorelease的

  • 系统自带的方法里面的方法名没有包含 alloc、new、copy等字眼,说明返回的对象都是autorelease的
    例如下面的都不用调用release
NSString *str = @"chenfanfang";
NSString *str2 = [NSString stringwithFormat:@"age is %d",23];
  • 开发中经常会提供一些方法(可以自己写的方法),快速创建一个已经autorelease过的对象。

    如下:

//注意:创建对象不要直接用类名,一般用self
+ (id)person
{
    return [[[self alloc] init] autorelease];
    //最好不用 return [[[Person alloc] init] autorelease];
}
  • 0
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值