Object-C总结

今天刚刚学完了oc,做一个总结,以方便以后自己查看。

==============================================================================================

第一部分  oc编程的整体框架和步骤

==============================================================================================

基本概念:oc是在c语言的基础上拓展的,所以大体框架跟c语言没有多大区别,最大的区别在于oc强调对象,所以有了类和对象的概念

  对象:某一个具体的事物都可以称为对象,但在oc中对象不能在没有类的情况下出现;

  类   :某一些具有相同特性和行为的对象的抽象描述,在oc中类大部分时候无法直接使用,需要通过对象来实现一些行为;

在oc编程中,我们需要首先从功能的角度出发剥离出来对象,并且根据对象抽象出类,然后创建类和对象,之后才能去实现功能;

  创建类的过程:创建类文件、声明、实现;

    创建的文件是一对文件,分为.h和.m文件;

    声明是在.h文件中编写的,分两部分:特征的声明和行为的声明,在oc编程中两者被称为实例变量和行为;

#import <Foundation/Foundation.h>

@interface Animal : NSObject
{
    NSString *_name;
}
-(void)eat;
@end

如上,其中_name为我们声明的实例变量,eat是我们声明的行为;

这里有几个要注意的地方:1)所有的实例变量声明都必须写在{}内,标准格式都应该在变量名前加上下划线;

            2)所有的方法都写在{}外面,方法分为两种,一种是 + 开头的称为类方法,在调用的时候只能由类来调用,如上图若是类方法那么调用的时候就需要 

               Animal来调用,另一种是 - 开头的称为实例方法,也可以叫做对象方法,顾名思义就是对象调用的;

            3)类的所有声明都写在@interface  @end这两者之间,就好比main函数中的代码不能出main函数的大括号一样;

    实现是在.m文件中编写的;

#import "Animal.h"

@implementation Animal
-(void)eat
{
    NSLog(@"最爱吃骨头");
}
@end

如上,这里对.h文件中写的eat方法进行了实现;

这里有几个要注意的地方:1)实例变量不需要实现,只有方法需要实现,且声明过的方法必须实现;

            2)方法类似于c语言中的函数,可以有返回值,也可以有参数,格式为          

                  -(返回值类型) 函数名 : (参数类型) 参数名;

               有多个参数时,每个参数前加上描述,之后用:调用,每两个参数调用之间用空格隔开或者回车隔开;

            3)所有的方法实现需要写在@implementation  @end 之间;

--------------以上是类的创建的过程,下来说对象的创建过程------------------

首先要说的一点是oc中对象的存储位置都是在堆区,之前c语言中学习过堆区的空间需要我们手动去开辟,这里系统提供给我们一个类方法:alloc

Animal *p = [Animal alloc];

开辟空间之后还无法直接使用,因为空间中放置的东西还不是我们要使用数据,所以这里我们需要初始化,系统提供给我们一个对象方法:init;

[p init];

这两步可以连在一起写出来

Animal *p = [[Animal alloc] init];

系统提供的初始化方法只是简单的将空间中的数据清零,保证不会在调用时出现乱七八糟的错误,但还不是我们需要的数据,那么我们需要将我们需要的数据放进去;

这里涉及到实例变量的可见度,这里具体讲解一下;

实例变量的可见度分为三种:@public,@private,@protected;

  @public:公开的,类内的方法,子类的方法,类外的调用都可以访问到,但破坏了oc的封装特性,所以一般不予使用;

  @private:私有的,类内可以访问到,子类和类外都无法访问;

  @protected:受保护的,类内和子类可以访问,在其他地方都无法访问;默认类型

实例变量可见度在声明实例变量的时候使用;

@interface Animal : NSObject
{
    @public
    NSString *_gender;
    @private
    NSInteger _age;
    @protected
    NSString *_name;
}
@end

如上,_gender是public可见度的,_age属于private可见度的,_name是protected可见度的;

我们常用的可见度是protected,也就是系统默认的可见度,那么怎么去访问类内的实例变量呢,系统提供我们一对方法:setter,getter方法

但这两个方法需要自己手动来写,格式如下:

#import <Foundation/Foundation.h>

@interface Animal : NSObject
{
    NSString *_name;
}
-(void)setName:(NSString *)name;
-(NSString *)name;

@end
#import "Animal.h"

@implementation Animal
-(void)setName:(NSString *)name
{
    _name = name;
}
-(NSString *)name
{
    return _name;
}
@end

通过写好的setter,getter方法我们就可以在main函数中访问到Animal内中的实例变量_name了;

使用方法有两种,一种是用[]调用方法,还有一种是点语言;

Animal *p = [[Animal alloc]init];
[p setName:@"小狗"];       //这里是通过[]来调用setter方法
NSLog(@"%@",p.name);  //这里是通过点语法来调用getter方法

这样一个基本的oc程序框架和过程就完成了;

-------------------------------下面来说一下自定义初始化和便利构造器-----------------------------

自定义初始化:系统提供的初始化方法只能简单的将空间内的所有数据置零,但很多时候我们创建一个对象之后所使用的值都不会是零,第一种解决办法就是在创建之后再去赋值,

       就是我们上面说的setter方法,而另外一种就是自定义初始化,在初始化的过程中我们就赋予我们想要的值;

下面看自定义初始化的写法:

#import <Foundation/Foundation.h>

@interface Animal : NSObject
{
    NSString *_name;
}
-(id)initWithName:(NSString *)name; 


@end
#import "Animal.h"

@implementation Animal

-(id)initWithName:(NSString *)name
{
    _name = name;
    return self;
}

@end
#import <Foundation/Foundation.h>
#import "Animal.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {

        Animal *p = [[Animal alloc] initWithName:@"小狗"];
    }
    return 0;
}

以上就是自定义初始化的声明、实现和调用;

便利构造器:便利构造器是基于封装思想产生的,主要作用是将alloc和init变成一步来完成;

下面看一下含有便利构造器的初始化:

#import <Foundation/Foundation.h>

@interface Animal : NSObject
{
    NSString *_name;
}
-(id)initWithName:(NSString *)name;

+(id)animalWithName:(NSString *)name;

@end
#import "Animal.h"
@implementation Animal

-(id)initWithName:(NSString *)name
{
    _name = name;
    return self;
}

+(id)animalWithName:(NSString *)name
{
    Animal *p = [[Animal alloc] initWithName:name];
    return p;
}
@end
#import <Foundation/Foundation.h>
#import "Animal.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {

        Animal *p = [Animal animalWithName:@"小狗"];
    }
    return 0;
}

从以上的代码来看:便利构造器名字以类名小写开头,后面与自定义初始化一样;

----------------------------继承--------------------------------------------------------------

在上面讲到了封装特性,那么这里来说oc语言的另一个特性:继承;

在我们创建的Animal类中可以看到这么一行代码

@interface Animal : NSObject

表示的意思就是Animal继承于NSObject,而Animal是NSObject的子类,NSObject是系统提供的类,也称为根类,即没有父类的类,最原始的类;

继承:继承表示子类拥有父类的所有实例变量和方法,能够调用所有的父类方法,访问所有非受保护的可见度的实例变量;

在了解了继承之后,我们可以改写父类的一些方法:

#import "Dog.h"
@implementation Dog
-(id)initWithName:(NSString *)name
{
    if (self = [super init]) {
        _name = name;
    }
    return self;
}
@end

这里我们创建了一个Dog类,继承于Animal,在这里重写了父类Animal的自定义初始化;

super:super是一个指针,代指父类,可以调用父类的方法;

self   :self也是一个指针,代指自己的类和对象,可以用来访问本类的实例变量,也可以调用本类的方法;

还有一点就是重新父类方法的时候不需要在子类的.h文件里再次声明,因为声明已经从父类那里继承过来了,只需要重写实现就可以了;

特别注意:继承不可以随便用,子类和父类必须在逻辑上存在包含关系,比如加菲猫和猫,加菲猫属于猫,所以加菲猫是猫的子类,如果不存在这样的包含关系,比如猫和狗,那么

     不可以用继承,如果一定要另一个类中的实例变量或者方法,那么可以使用#import;

==============================================================================================

第二部分  oc中的类类型和常用功能

==============================================================================================

oc中常用的类类型有字符串,数组,字典,下面来详细说明一下;

(1)字符串

  字符串分为两类,不可变字符串NSString和可变字符串NSMutableString;

  1.不可变字符串NSString

    NSString初始化共有7种方法,其中有两种没有在初始化中给予赋值,NSString是不可变字符串,所以这两种方法没用,下来介绍剩下5种初始化方法;

  NSString *p1 = @"dsada"; //字面量初始化赋值方式;
  
  NSString *p2 = [NSString stringWithFormat:@"%@",p1]; //通过Format初始化赋值
  NSString *p3 = [[NSString alloc] initWithFormat:@"%@",p1];
        
  NSString *p4 = [NSString stringWithString:p1]; //通过字符串给字符串初始化
  NSString *p5 = [[NSString alloc] initWithString:p1];
        
  NSString *p6 = [NSString stringWithUTF8String:"asd"]; //常用来将c语言的字符串转换成对象类型的字符串;
  NSString *p7 = [[NSString alloc] initWithUTF8String:"da"];

    下面介绍一些NSString常用的方法:

        [p1 length];    //取字符串长度;

  [p1 compare:p2];//p1和p2比较;p1大,返回值大于1;p2大,返回值小于1;一样大,返回0;
   [p1 isEqualToString:p2];//p1和p2是否相同,相同返回1,不同返回0;
        
   [p1 hasPrefix:@"11"];//判断前缀
   [p1 hasSuffix:@"00"];//判断后缀
        
   [p1 uppercaseString];//全转化成大写
   [p1 lowercaseString];//全转化成小写
   [p1 capitalizedString];//首字母转化成大写,其余小写
        
   [p1 substringToIndex:2];//截取字符串,范围为[0,2)
   [p1 substringFromIndex:2];//截取字符串,范围为[2,最后]
   [p1 substringWithRange:NSMakeRange(1, 2)];//截取字符串,范围是[1,1+2)
        
   [p1 stringByAppendingString:p2];//将p2拼接到p1后面
   [p1 stringByAppendingFormat:@"%@",p2];
        
   [p1 stringByReplacingOccurrencesOfString:@"2" withString:@"s"];//将p1中所有的2替换成s
   [p1 stringByReplacingCharactersInRange:NSMakeRange(1, 2) withString:@"aa"];//范围[1,1+2)内的字符全替换成“aa”

提醒:NSString是不可变数组,所以一切对NSString有更改的操作都必须有一个新的字符串来接收,上述方法中没有写到这一点,这里特别提醒!!!!

   NSString是不可变数组,所以一切对NSString有更改的操作都必须有一个新的字符串来接收,上述方法中没有写到这一点,这里特别提醒!!!!

   NSString是不可变数组,所以一切对NSString有更改的操作都必须有一个新的字符串来接收,上述方法中没有写到这一点,这里特别提醒!!!!

   重要的事说三遍;

  2.可变字符串NSMutableString

        NSMutableString *p1 = [[NSMutableString alloc] init];
        NSMutableString *p2 = [NSMutableString string];//初始化方法,一般这样使用,也可以在初始化过程中赋值
        
        [p1 setString:@"sss"]; //赋值
        
        [p1 appendString:@"456"];//拼接
        
        [p1 deleteCharactersInRange:NSMakeRange(1, 2)];//删除[1,1+2)范围内的字符
        
        [p1 insertString:@"aa" atIndex:0];//在第0个字符的位置插入“aa”

提醒:可变字符串NSMutableString属于不可变字符串NSString的子类,所以NSString的方法NSMutableString都可以调用

(2)数组

  1.不可变数组NSArray

        NSArray *arr1 = [[NSArray alloc] init];
        NSArray *arr2 = [NSArray array];//这两种没用,因为不可变
        
        NSArray *arr3 = [[NSArray alloc] initWithObjects:@"13",@"sa", nil];
        NSArray *arr4 = [NSArray arrayWithObjects:@"gs",@"11", nil];//直接赋值的初始化方法
        
        NSArray *arr5 = [[NSArray alloc] initWithArray:arr1];
        NSArray *arr6 = [NSArray arrayWithArray:arr2];//通过数组给数组赋值;
        
        NSArray *arr7 = @[arr1,arr2];//字面量初始化方式
        
        [arr7 objectAtIndex:1];//通过下标取值
        arr7[1];//这样的取值方式也可以
        
        [arr7 count];//计算元素个数
        [arr7 indexOfObject:arr1];//根据元素找出下标;

  

  2.可变数组NSMutableArray

        NSMutableArray *mar1 = [[NSMutableArray alloc] init];
        NSMutableArray *mar2 = [NSMutableArray array];//可变数组的初始化方式比较随意,因为可以随意的更改内部数据
        
        [mar1 addObject:@"da1"];//添加元素,一次只能添加一个
        [mar1 addObject:@"da2"];
        [mar1 addObject:@"da3"];
        
        
        [mar1 removeObject:@"da1"];//删除给定元素
        [mar1 removeObjectAtIndex:1];//删除下标为1的元素
        [mar1 removeLastObject];//删除最后一个元素
        [mar1 removeAllObjects];//删除所有元素
        
        [mar1 insertObject:@"1" atIndex:2];//插入元素,在下标为2的位置插入1
        
        [mar1 replaceObjectAtIndex:1 withObject:@"a"];//替换,将下标为1处的元素替换成后面的a
        
        [mar1 exchangeObjectAtIndex:0 withObjectAtIndex:1];//交换,将两个下标元素进行交换位置

(3)字典

  定义:是一个数据容器,里面存放的值都是一对一对的,称为键值对,键-key,值-value,通过key值获取value,不能通过value获取key,也无法直接通过字典取到value,只能通过字典取到key,再取到value;字典中的key值作为标识不能重复,但value作为被存储的对象可以重复;字典是乱序排列的,不可以进行排序;

  1.不可变字典NSDictionary

        NSDictionary *dict1 = [[NSDictionary alloc] initWithObjectsAndKeys:@"obj2",@"key2",@"obj1",@"key1", nil];
        NSLog(@"%@",dict1);//直接用键值对赋值,顺序为value,key
        
        NSDictionary *dict2 = [[NSDictionary alloc]initWithObjects:@[@"val2",@"val1",@"val3"] forKeys:@[@"k2",@"k1",@"k3"]];//此方式需要注意key和value的对应顺序不能错误
        NSLog(@"%@",dict3);
         
        NSDictionary *dict3 = @{@"ke1":@"o1",@"ke2":@"o2",@"ke4":@"o4",@"ke3":@"o3"};
        NSLog(@"%@",dict4);//字面量赋值

        NSInteger a = (NSInteger)[dict4 count];//计算元素个数
         
        NSArray *arr1 = [dict4 allKeys];//获取所有key存放到一个数组中,且只能使用不可变数组来接收
        
        NSArray *arr2 = [dict4 allValues];//获取所有value存放到一个数组中,也只能使用不可变数组接收
         
        NSString *va1 = [dict4 valueForKey:@"ke2"];//通过key值获取value
        NSString *va2 = [dict4 objectForKey:@"ke2"];

  2.可变字典NSMutableDictionary

        //初始化
        NSMutableDictionary *mdic1 = [NSMutableDictionary   dictionary];
        
        //添加键值对
        [mdic1 setObject:@"周一" forKey:@"1"];
        [mdic1 setObject:@"周二" forKey:@"2"];
        //set方法用来修改和添加键值对,如果键值不存在,那么是添加,如果键值已存在,那么是修改;
        
        //字典的遍历打印
        NSArray *arr = [mdic1 allKeys];
        for (int i =0; i<[mdic1 count]; i++) {
            id temp =[arr objectAtIndex:i];
            id value = [mdic1 valueForKey:temp];
            NSLog(@"%@ = %@",temp,value);
        }//注意对字典进行遍历,得到的是key值,我们只能通过key值访问value

        //删除
        [mdic1 removeAllObjects];

这里的方法介绍的比较简单,基本的添加,取值,删除等功能在这些系统提供的类中都有,且有很多种形式,这里不一一列举,在使用过程中可以进系统头文件查找;

(4)集

  集中所装的数据必须是对象,但不强调类型,可以是字符串,也可也是数组;集中的数据存储是乱序的,不能进行排序;在输出时重复的数据会重叠,显示出来数量,所以集中的元素个数是集中不同元素的个数;

  1.不可变集NSSet

        //创建集合
        NSSet *set = [[NSSet alloc] initWithObjects:@"1",@"2", @"3",@"4",@"5",nil];
        NSLog(@"%@",set);
        
        //求个数
        NSLog(@"%ld",[set count]);
        //判断是否包含某一个对象,存在返回YES,不存在返回NO
        [set containsObject:@"1"];
        //判断是否存在某一个对象,存在返回该对象,不存在返回空
        NSString *p = [set member:@"s"];
        NSLog(@"%@",p);
        
        //获取集合中任意一个对象
        NSLog(@"%@", [set anyObject]);
        
        //可以将集合中所有内容添加到数组中
        NSArray *ar = [set allObjects];
        NSLog(@"%@",ar);

  2.可变集NSMutableSet

NSMutableSet *mset = [NSMutableSet set];
        
        //将数组添加到集
        [mset addObjectsFromArray:ar];
        
        //删除
        [mset removeAllObjects];

  3.计数集NSCountedSet

        NSCountedSet *countSet = [NSCountedSet setWithObjects:@"木木",@"青青",@"木木", nil];
        NSLog(@"%ld", [countSet countForObject:@"木木"]);
        NSLog(@"%@",countSet);//可以计算出集中一个元素重复的次数

(5)日期NSDate

        NSDate *date = [NSDate date];
        NSLog(@"%@",date); //定义一个日期,打印出来是0时区的对应时间
        

    //获取当前所在时区准确时间的方法
        NSTimeZone *zone = [NSTimeZone localTimeZone];
        NSLog(@"%@",zone);//获取当前所在的时区
        
        NSInteger second = [zone secondsFromGMT];//计算出当前时区与0时区的时差
        
        NSDate *nowDate = [NSDate dateWithTimeIntervalSinceNow:second];//这里nowDate的类型使用NSTimeInterval也可以,这个时NSDate的子类
        NSLog(@"%@",nowDate);//将差值补充到0时区时间上,得出所在时区正确时间
        
        
        NSDate *date1 = [NSDate dateWithTimeIntervalSinceReferenceDate:3600];//这个方法是定义一个时间方法从2001年1月1日的0点格林尼治时间开始过去3600秒的那个时间
        
        
    //设置显示的日期格式
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];//创建一个日期格式类,只能这样创建,没有便利构造器
        
        [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];//规定日期格式,其中需要注意月份MM要大写,小时HH大写的话表示24小时制,小写hh表示12小时制
        
    //字符串和日期之间的相互转化
        NSString *str = @"12041215113844";
        
        NSString *dateStr = [formatter stringFromDate:date1];//将日期格式转化成字符串
        
        NSDate *strDate = [formatter dateFromString:str];//将字符串转化成日期格式

(6)类的扩展

  1.类目

    类目是创建一对文件对一个类的方法进行增加,无序被扩充类的代码;

    创建过程:commond+n,选择Source中的Objective-C File,之后的File Type选择Category,Calss选择你要扩充的类,输入名字之后创建;

  以下几点需要注意:

          1)类目中新添加的方法如果与原类中的方法发生矛盾,那么新添加的方法优先级高于原类中的方法

          2)分类中定义的方法属于类,因此分类中新定义的方法,不能与原类中的方法重名;

          3)分类中只能添加方法,不能添加实例变量;

          4)分类包含两部分:声明和实现;

          5)分类中的方法可以被原类的子类所继承;

  

  2.延展

    延展是对自己所创建的类进行的扩充

    创建过程:commond+n,选择Source中的Objective-C File,之后的File Type选择Extension,Calss选择你要扩充的类,输入名字之后创建;

  以下几点需要注意:

    1)延展只有一个.h文件,其中只写需要扩充的实例变量和方法的声明,实现写到原类的.m文件中;

    2)一般建议直接在原类的.m文件实现借口上方写入一个声明借口来写延展内容,而不是建立专门的延展文件;

  3.协议和代理

    协议:Protocol,是一个.h文件,里面一堆方法的声明;协议的使用需要两个对象,一个发布任务者,另一个接收协议者;

    创建过程:commond+n,选择Source中的Objective-C File,之后的File Type选择Protocol,这里与之前不一样不需要指定扩充的类,只需输入名字创建完成;

  下面看一个协议的实例:

@protocol MarryProtocol <NSObject>
/*
协议中的方法分为两种
 1.必须实现的方法  @required(默认的也是这个)
 2.可选择实现的方法 @optional
*/

@required
-(void)makeMoney;
-(void)washCloth;
-(void)cook;
-(void)careKid;

@optional
-(void)noSmoking;
-(void)noDrink;

上面是协议的内容,下面是两个对象需要使用协议的时候的一些改动

@interface Boy : NSObject<MarryProtocol> //接收协议者在声明接口的父类后面添加上协议的名字
@interface Girl : NSObject
{
    NSString *_name;
    NSString *_hobby;
    NSInteger _age;
    NSString *_husband;
    
    //发布任务者需要在实例变量里面定义一个代理,代理的类型就是协议的内容
    id<MarryProtocol> _delegate;
}

以上这些确定以后,需要在接收协议的类的.m文件中实现协议中的方法,并在发布任务者的对应方法中写入想要调用到的协议方法

-(void)noWash
{
    NSLog(@"%@:我不想洗衣服",_name);
    [_delegate washCloth];
}

最后是在main函数中的调用

        Boy *boy = [Boy boyWithName:@"陈家洛" hobby:@"耍贱" age:0 wife:nil];
        Girl *girl = [Girl girlWithName:@"朱丽叶" hobby:@"吟诗" age:0 husband:nil];//创建对象
        [girl setDelegate:boy];//将接收协议者设置为发布任务者的代理
        
        //之后就是调用发布任务者的方法,从而通过协议让接收协议者来完成任务
        [girl setHusband:boy.name];
        
        [girl noWash];

(7)枚举器和block方法块

  1.枚举器

    枚举器是系统提供的一种快速按顺序取出集合中元素的方法;

        NSArray *arr = @[@"q",@"w",@"e",@"r",@"t"];
        NSEnumerator *enum1 = [arr objectEnumerator];//将数组装进枚举器
        
        id obj;
        while (obj = [enum1 nextObject]) {
            NSLog(@"%@",obj);//取出数组中元素的顺序与数组中的顺序是一样的
        }
        
        NSDictionary *dict = @{@"name":@"端木岐",@"age":@"12",@"hobby":@"抓泥鳅",@"gender":@"",@"address":@"神山"};
        
        NSEnumerator *enum2 = [dict objectEnumerator];//将字典装进枚举器
        while (obj = [enum2 nextObject]) {
            NSLog(@"%@",obj);//字典在枚举器中取出的不是key,而是value,这点需要注意
        }

  2.block方法块

    block在oc中使用最多的地方在数组的排序中,系统提供的排序方式只有顺序排序,而且是直接对象比较,但大多时候需要比较的是对象中的实例变量,那么系统提供的方法就不能使用了,这时候就需要使用block来写信的排序方法;

NSArray *stuarr1 = [arr sortedArrayUsingSelector:@selector(compareByStudentName:)];

这是block的第一种使用,在调用选择器的排序方法中使用,其中的compareByStudentName:需要在student类中实现;

-(NSComparisonResult)compareByStudentName:(Student *)aStudent
{
    if ([_name compare:aStudent.name] < 0 ) {
        return NSOrderedAscending;
    }
    else if ([_name compare:aStudent.name] > 0){
        return NSOrderedDescending;
    }
    else {
        return NSOrderedSame;
    }
}

       这是第一种实现,还有第二种比较简单

-(NSComparisonResult)compareByStudentName:(Student *)aStudent
{
    return [_name compare:aStudent.name];
    
}

block的第二种使用是在直接嗲用block的排序方法中使用:

NSArray *stuarr = [arr sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
            return  [[obj1 name] compare:[obj2 name]];
        }];

到这里对oc的类类型和常用的工具介绍了一部分,但大部分还是没有说道,需要在使用中查阅系统说明;

==============================================================================================

第三部分  内存管理

==============================================================================================

 (1)内存管理机制

  内存管理机制:引用计数

  引用计数增加的方法:

     +alloc:开辟空间,由于开辟出来的空间需要有一个指针来接收,所以等价于引用计数+1,而且开始引用计数为0,所以alloc是从0到1的过程;

       -retain:一个对象方法,使该对象所在空间的引用计数+1;

     -copy  :将某对象拷贝一份到新的空间,被拷贝对象引用计数不改变,新空间由于是新开辟的,所以新空间的引用计数类似alloc,是从0到1的过程;

  引用计数减少的方法:

     -release:一个对象方法,使该对象所在空间的引用计数-1;

     -autorelease:引用计数-1,但是在出了自动释放池之后才执行;

   注意:1.在指针的直接赋值过程中 Student *p = p1,类似assign一样不会增加引用计数,但为了规范使用应该这样书写Student *p = [p1 retain],这样不仅完成赋值,引

      用计数还+1

            2.使用alloc,retain,copy使引用计数增加的话那么就必须要有release,autorelease来减少计数,增加量和减少量应该一致;  

(2)实例变量的属性即属性中的内存管理

  属性:关键字property,通过属性声明的实例变量不需要再写setter和getter方法;

@interface Person : NSObject
{
    NSString *_name;
    NSString *_gender;
    NSInteger _age;
    NSString *_address;
}
@property(nonatomic,retain)NSString *name;
@property(nonatomic,retain)NSString *gender;
@property(nonatomic,assign)NSInteger age;
@property(nonatomic,copy)NSString *address;

以上就是属性的声明,属性还有实现(关键字synthesize),但也可以不用写;

属性后面的括号中写的是属性的属性,有三种属性,分别是读写属性(默认的是readwrite,还有一种是readonly只读)、原子属性(原子性atomic可以保证线程安全但是耗费内存,另一个是nonatomic不保证线程安全但内存消耗少,默认的是atomic,一般使用我们需要改为nonatomic)、语义属性(分为三种:针对基本数据类型和c语言类型的assign、专门用于对象的retain、用于遵循了<NSCopying>协议的对象的copy,默认的是assign,这个属性我们需要根据实例变量的类型来更改);

(2)属性的内存管理

  上面说到属性有三种语义属性:assign,retain和copy,他们之所以针对的类型不同,正是因为在内存方面的机制不同,而这方面是体现在setter和getter方法上的;

  assign:其中的setter、getter就是正常的赋值和返回值,因为大部分基本数据类型的数据都是存在于常量区的,常量区的数据引用计数是不可估计的,所以引用计数对此无效;

  retain:其中的setter在赋值的时候,参数要retain一次,因为给对象字符串赋值,那么参数也应该是一个对象字符串,在这个过程中参数的引用增加了,那么相应的就应该增加参数的引用计数,所以retain;getter方法中返回值要retain一次,因为返回的也是一个对象字符串,而返回值需要一个对象来接收的,接收之后返回值的引用也增加了,所以要retain来增加返回值的引用计数;

  copy:其中的setter在赋值的时候,参数要copy一次,因为copy的赋值过程就是将参数复制到一个新的空间中,所以要copy,要不然新空间引用计数也无法变为1;getter方法同retain,是要返回返回值的retain,原因也是一样;

注:ios的内存是有两种模式的,一种是ARC自动管理,一种是MRC手动管理,上面说到的都是MRC中需要注意的地方,如果是ARC那么不需要我们去考虑内存问题;

原文地址:https://www.cnblogs.com/yuyong-2015/p/4859692.html