zoukankan      html  css  js  c++  java
  • 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那么不需要我们去考虑内存问题;

  • 相关阅读:
    [LeetCode] 139. Word Break 单词拆分
    [LeetCode] 140. Word Break II 单词拆分II
    [LeetCode] 297. Serialize and Deserialize Binary Tree 二叉树的序列化和反序列化
    [LeetCode] 206. Reverse Linked List 反向链表
    [LeetCode] 92. Reverse Linked List II 反向链表II
    [LeetCode] 258. Add Digits 加数字
    [LeetCode] 66. Plus One 加一
    [LeetCode] 21. Merge Two Sorted Lists 合并有序链表
    [LeetCode] 88. Merge Sorted Array 合并有序数组
    [LeetCode] 10. Regular Expression Matching 正则表达式匹配
  • 原文地址:https://www.cnblogs.com/yuyong-2015/p/4859692.html
Copyright © 2011-2022 走看看