OC进阶(一)

1.OC的内存管理

     ---任何对象都有可能拥有一个或者多个拥有者,只要一个对象至少还有一个拥有者,它就会继续存在

    

/*
 
    1 引用计数器:
        用来保存当前对象有几个东西在使用它(数字)
 
    2 引用计数器的作用:
        用来判断对象是否应该回收内存空间(如果对象不等于nil,当引用计数器为0,此时要回收对象的内存空间)
 
    3 引用计数器的操作:
 
        retain    使得引用计数器+1
        release   使的引用计数器-1
        retainCount  得到引用计数器的值
 
    4 如果一个对象被释放的时候,会有"临终遗言"(会调用该对象的dealloc方法)
      
        注意:
         1)dealloc方法是NSObject 的,一般我们要重写dealloc方法
         2)在dealloc 方法的内部,要调用 [super dealloc];
 
    5 手动内存管理:
   
        MRC 手动内存管理
        ARC 自动内存管理
 
        注意,我们创建一个项目,此时默认的是ARC的(自动内存管理)
        把项目从ARC模式改成 MRC模式
       
 */
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        //用Person 类实例化一个实例对象
        Person *p = [Person new];  // 对象有没有所有者?  有
//        [p dealloc];
        //证明有一个所有者
        NSUInteger count = [p retainCount];
        
        NSLog(@"count = %lu",count);  // 1
        
        //使用引用计数器+1
//        Person *p2 = p;            //
        //Person *p2 = [p retain];
        [p retain];
        NSLog(@"p.retainCount = %lu",[p retainCount]);   //2
        
        //如果要回收对象?  应该想办法 retatinCount = 0
        [p release];
        NSLog(@"p.retainCount = %lu",[p retainCount]);   //1
        
        [p release];  //此处执行后,p的空间被回收             //0
        
        //证明p的空间被释放了,可以在在Person类中,重写dealloc方法
    }
    return 0;
}
@implementation Person
//dealloc方法,是对象的临终遗言的方法
//对象被销毁的时候,会默认的调用该方法
//注意:dealloc 方法是系统根据引用计数器的值,自动调用的,
//不需要手动调用
- (void)dealloc
{
    //1 先释放子类自己的对象的空间
    NSLog(@"Person已经挂了");
    //2 再释放父类的
    [super dealloc];
}
@end

2.内存管理的原则

/*
   内存管理:
 
        对象如果不在使用了,就应该回收它的空间,防止造成内存泄露
 
   内存管理的范围:
 
        所有的集成了NSObject的对象的内存管理
 
        基本数据类型(int double float char struct enum )的数据内存不需要我们进行管理
 
   内存管理的原则:
 
    一.
        如果对象有人使用,就不应该回收
        如果你想使用这个对象,应该让这个对象 retain一次
        如果你不想使用这个对象了,应该让这个对象 relase一次
 
    二.
    
        谁创建 谁release
 
    三. 
 
        谁 retain 谁 release
 
    内存管理研究的内容:
 
       1)野指针:  1)定义的指针变量没有初始化     2)指向的空间已经被释放了
 
       2)内存泄露: 
   
             {
             Person *p  = [Person new];
 
             }
 
 
             p 栈区
             [Person new];  堆区
 
             如果栈区的p已经释放了,而堆区的空间还没有释放,堆区的空间就被泄露了
 
 
 
 */

#import <Foundation/Foundation.h>
#import "Dog.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //创建:  new    alloc init   copy
        Dog *bigYellowDog = [Dog new];   //1
        NSLog(@"bigYellowDog.retainCount = %lu",[bigYellowDog retainCount]); //1
        
        //
        Dog *jd = [bigYellowDog retain];  //jd  2    bigYellowDog  2
//        [jd run];
        NSLog(@"bigYellowDog.retainCount = %lu",[bigYellowDog retainCount]); //2
        NSLog(@"jd.retainCount = %lu",[jd retainCount]); //2
        [bigYellowDog release];  //1
        
        //只有保证谁创建谁release 才能保证对象能够释放
        [bigYellowDog release];  //0
    }
    return 0;
}

3.野指针

/*
 
    一定要注意:
 
        每个Target创建完成以后,默认的都是ARC模式
 
 */


#import <Foundation/Foundation.h>
#import "Dog.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
    
        //创建一个对象
        //对象创建完成以后,默认的所有者有一个,是自己,所以引用计数为1
        Dog *byd = [Dog new];  //1
        [byd eat];  //
        NSLog(@"byd.retainCount = %lu",byd.retainCount);
        //如果一个对象已经被释放了,这个对象就称之为僵尸对象
        //
        [byd release];         //0
//        NSLog(@"byd.retainCount = %lu",byd.retainCount);  //值已经没有意义了
        //这句话默认情况下不报错,
        //如果要让他报错,要开启僵尸对象检测
        //byd指针也就是野指针
//        [byd eat];   //野指针访问
        
//        [byd retain];  //byd 已经是僵尸对象了,不能复生
        
    }
    return 0;
}

4.单个对象的内存管理

#import <Foundation/Foundation.h>
#import "Dog.h"
//避免僵尸对象的使用报错
void test(){

    Dog *d = [[Dog alloc] init];  //1
    
    [d release];  //1--->0
    
    //nil 给nil发送任何消息,都没有效果
    //        [nil run];
    //避免使用僵尸对象的方法是,对象释放了以后,给对象赋值为nil
    //健壮性
    d = nil;  //nil 给对象赋值  Nil 类对象
    
    //
    [d retain]; //

}
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        //单个对象的内存泄露问题
        //内存泄露情况1:
        // 创建完成 使用之后,没有release
//        Dog *d = [[Dog alloc] init];  //1
//        NSLog(@"%lu",d.retainCount);  //使用d对象
       
        //内存泄露情况2:
        //没有遵守内存管理的原则
//         Dog *d = [[Dog alloc] init];   //1
//         [d retain];  //2
////        [d release];
//         [d release]; //1
        
        //内存泄露的情况3:
        //不当的使用了nil
//        Dog *d = [[Dog alloc] init];   //1
//        d = nil;
//        
//        [d eat];  //nil eat
//        [d release]; // nil release
        
        //内存泄露的情况4:
        //在方法中对传入的对象进行了retain
        Dog *d = [[Dog alloc] init];  //1
        NSLog(@"d.retainCount = %lu",d.retainCount);
        //对象依然被泄露了
        [d compareColorWithOther:d];  //2
        NSLog(@"d.retainCount = %lu",d.retainCount);
        
        [d release];
        
        
    }
    return 0;
}

5.多个对象的野指针错误

#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        //创建对象
        Person *fengjie = [Person new];    //1
        Car *bigBen = [Car new];           //1
        bigBen.speed = 100;
        
        //给凤姐一辆车
        [fengjie setCar:bigBen];
        [fengjie goLasa];
        [fengjie goLasa];
        //
        [bigBen release];     //1->0  car销毁  车已经毁了

        [fengjie goLasa];  //  车已经毁了 人就不能去拉萨了  所以报错
        
        [fengjie release];
        
        
    }
    return 0;
}

6.多个对象的内存管理

#import "Person.h"

@implementation Person

- (void)dealloc
{
    
    //为什么要让车先挂
    //_car  ---> 1

   [_car release];   //_car  0 销毁
    NSLog(@"人已经挂了");
    [super dealloc];
}

-(void)goLasa{

    //开车
    // [nil run];
    [_car run];

}
-(void)setCar:(Car *) car{
    //bigBen  1
 
    _car = [car retain];  //benBen   _car   2

}
@end

7.set方法的内存管理

//set方法的内存管理
//原则:
//如果在一个类中,有其他类的对象(关联关系)
//set方法书写的时候,要:判断是否是同一个对象,release 旧值,retain新值
-(void)setCar:(Car *) car{
   
    //为了解决set方法的内存泄露,我们在新的retain之前,先release旧值
//    [_car release];
//    _car = [_car retain];
    NSString
    
    
    
    
    //car  bmw
    // 如果 _car == car 这是同一个对象
    // 如果是同一个对象就不需要在release
    if (_car != car) {
        
        [_car release];  //[bmw release]  1-->0
        _car = [car retain];  //  _car   bmw  2
    }
    

}
@end

8.@property参数

#import <Foundation/Foundation.h>
#import "Car.h"
@interface Person : NSObject
@property(nonatomic,retain) Car *car;
/*
 assign 直接赋值
 -(void)setCar:(Car *)car{
 
     _car = car;   
 
 }
 
 验证: assign
 
 
 
 retain release 旧值,retain新值
 //什么时候使用retain 
 //在一个类中有关联其他对象的时候,这个对象的 @property (nonatomic,retain) 
 
 //什么时候使用assign
 //实例变量是基本数据类型的时候
 
 -(void)setCar:(Car *)car{
 
     if(_car != car){
 
       [_car realase];
       _car = [car retain];
 
    }
 }

 */
@property(nonatomic,assign) int age;

-(void)driver;
@end
#import <Foundation/Foundation.h>

@interface Person : NSObject
@property (nonatomic,assign,setter=isVip:,getter=isVip) BOOL vip;  //_vip
@end
#import <Foundation/Foundation.h>
#import "Dog.h"
#import "Person.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        Dog *d = [Dog new];
        // d.tuiNum ----> [d setTuiNum:4];
//        d.tuiNum = 4;  //调用set方法
        NSLog(@"tuiNum = %d",d.tuiNum);  //调用get方法
        
        Person *p = [Person new];
        //isVip
        //setVip
//        p.vip = YES;
        //1)替换set方法的名称  @property (nonatomic,setter = isVip:)
//        [p setVip:YES];   //-----  [p isVip:YES];
        //2)替换get方法的名称  @property (nonatomic,setter = isVip:,getter=isVip)
        [p isVip:YES];
        
        //[p isVip]
        if (p.isVip) {
            NSLog(@"这时VIP客户");
        }
        
    }
    return 0;
}

9.@class的使用

/*
 
   @class的使用:
 
     @class的使用格式:  @class 类名;
     
     @class XXXX;
     含义: 告诉编译器,XXXX 是一个类,至于类有哪些属性和方法,此处不去检测
     好处: 如果XXXX文件内容发生了改变,而不需要重新编译
 
 
     @class 的使用注意:
 
     1) .h  @class  XX;
     2) .m  #import "XX.h"
 
     
     @class的特殊用法: 可以解决循环引入问题
 
 
 */

10.循环retain

/*
    
   循环的retain 会导致两个对象都会内存泄露
 
   防止方法:
 
    1) 让某个对象多释放一次 (注意顺序)
 
    2) 推荐的方法:
 
        一端使用 assign   一端使用retain 
 
 
 
 
 */

11.NSSting的内存管理

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        //定义字符串
        //字符串的常量池,
        //如果你需要的字符串在常量池中已经存在了,不会分配内存空间
        //使用字符串的时候,
        // @"abc"  stringWithString    alloc initWithString  都在常量区/\
        
        //0x100001030 小
        NSString *str1 = @"abc";   //@"abc" 字符串的常量
        NSString *str3 = [NSString stringWithString:@"abc"];   //常量区
        NSString *str5 = [[NSString alloc] initWithString:@"abc"]; //也在常量区
        NSString *str6 = [[NSString alloc] init];//常量区
        str6 = @"abc";

        
        //0x100202030 大
        //如果在常量区 str2  str4 地址应该是一样的
        //实际上不一样的,所以 str2 str4都在堆区
        NSString *str2 = [NSString stringWithFormat:@"abc"];   //不是在栈区,在堆区
        NSString *str4 = [[NSString alloc] initWithFormat:@"abc"];//不是在栈区,在堆区
        
        
        //0x7fff5fbff764
        int a = 10;  //栈区
        
        NSLog(@"str1 = %@,%p,%lu",str1,str1,str1.retainCount);
        NSLog(@"str2 = %@,%p,%lu",str2,str2,str2.retainCount);
        NSLog(@"str3 = %@,%p,%lu",str3,str3,str3.retainCount);
        NSLog(@"str4 = %@,%p,%lu",str4,str4,str4.retainCount);
        NSLog(@"str5 = %@,%p,%lu",str5,str5,str5.retainCount);
        NSLog(@"str6 = %@,%p,%lu",str6,str6,str6.retainCount);
        NSLog(@"a = %p",&a);
        
        
    }
    return 0;
}

11.autorelease的基本使用

/*
   自动释放池 :特殊的栈结构
   特点:  对象可以加入到自动释放池中
         自动释放池结束的时候,会给池中的对象发送一条 release消息
 
   自动释放池的使用:
 
    1) 创建自动释放池
      @autoreleasepool {
     
      }
 
    2) 加入自动释放池
       在自动释放池中
      [对象  autorelease];
 
 */

#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
    //1 创建自动释放池
    Person *p = [Person new];  // p  1
    @autoreleasepool {//自动释放池开始
        
        [p run];
 
        NSLog(@"%lu",p.retainCount); // 1
        
        // [p autorelease] 把对象p加入到自动释放池中
        // 注意:加入到自动释放池中以后, 引用计数不会变化
        [p autorelease];  //加入自动释放池,
        NSLog(@"%lu",p.retainCount); // 1
        
        [p run];
        
    }//自动释放池结束   [p release];
    [p run];
    return 0;
}

 

posted @ 2015-10-27 20:36  ios开发那点事  阅读(216)  评论(0编辑  收藏  举报