zoukankan      html  css  js  c++  java
  • OC学习心得【适合初学者】

    一、类和对象

    1.OC语言是C语言的扩充,并且OC是iOS和OS X操作系统的编程语言。

    ①具备完善的面向对象特性:

    封装:将现实世界中存在的某个客体的属性与行为绑定在一起,并放置在一个逻辑单元内

    继承:子类自动共享父类数据结构和方法的机制,这是类之间的一种关系

    多态:指相同的操作或函数、过程可作用于多种类型的对象上并获得不同的结果

    ②内存管理机制有GC(垃圾回收机制)和RC(引用计数机制【MRC和ARC】),但是iOS支持两种内存管理方式:ARC和MRC。

    ③面向对象的核心思想是类和对象

    2.类:具有相同特征和行为的事物的抽象。【是对象的类型】

    ①类的定义分为:接口部分和实现部分

    <1>接口部分:对外声明类的特征和行为。(.h文件中)

    标志:@interface...@end【作用;声明类名,类的继承(父类),遵循的协议,实例变量和方法(特征和行为)】

    <2>实现部分:行为的具体实现。(.m文件中)

    标志:@implementation...@end【方法的实现】

    3.对象:类的实例

    ①对象的创建

    <1>分配内存空间:堆区分配内存,并返回首地址。

    <2>初始化:为对象的实例变量设置初始值【init将内存空间的数据清零】

     1 Teacher *teacher = [[Teacher alloc] init]; 

    指针(teacher)存储对象的首地址,代指对象,进行操作

    4.instancetype和id区别

    ①instancetype可以返回和方法所在类相同类型的对象,id只能返回未知类型的对象。

    ②instancetype只能作为返回值和参数,id还可以用来定义变量

    5.实例变量

    ①实例变量变量可见度

    @interface Child : NSObject
    {
        @public// 作用范围:外部、内部、继承
        NSString *_name;
        @protected// 【系统默认】作用范围:内部、继承
        NSInteger _age;
        @private// 作用范围:内部
        CGFloat _score;
    }
    @end

    ②实例变量必须在花括号内

    6.方法

    ①类中不能出现同名方法

    ②“:”标识参数,不能省略。有冒号必须有参数。

    ③冒号属于方法名的一部分

    ④“+”类方法

    ⑤“-”实例方法

    // 方法名:printNum:
    // 有参(1个参)无返回值
    - (void)printNum:(NSInteger)num;
    // 有两个参数
    // 方法名:printName:age:
    - (void)printName:(NSString *)name age:(NSInteger)age;
    
    //有三个参数
    //方法名:printName:age:score:
    - (void)printName:(NSString *)name age:(NSInteger)age score:(CGFloat)score;

    7.在OC中使用消息发送机制:[receiver message]

    表述:

    ①给received对象发送message消息

    ②received接收到消息,即方法message

    ③teacher找到message方法,并执行。

    8.setter和getter方法

    ①setter(设置器)

    <1>格式

    - (void)setAge:(NSInteger)age;

    即set+首字母大写的实例变量名(忽略下划线)

    ②getter(访问器)

    <1>格式

    - (NSInteger)age;

    即返回值类型与变量类型一致,方法名与实例变量名相同(忽略下划线)

    ③无论setter还是getter内部操作的是实例变量

    ④每一个实例变量都需要一对setter和getter方法

    ⑤点语法只对setter和getter方法有效

    9.@class的作用

    只是表示有这么个类型,其他什么都没做。【注意:一般在.h中使用@class,在.m中引入头文件】

    二、初始化方法

    1.继承

    ①保证类的完整,简化代码

    ②NSObject是根类

    ③继承的内容:所有实例变量和方法

    ④继承是单向的,不能相互继承

    ⑤继承具有传递性

    ⑥子类可以重写父类的方法

    2.super

    ①编译器指令,并非对象

    ②作用:给super发消息,可以执行父类该方法的实现

    3.self

    ①系统关键字

    ②self在方法中指代当前方法的调用者

    在实例方法中,指代调用当前方法的对象

    在类方法中,指代当前类

    4.初始化方法

    ①作用:为某些实例变量赋初值

    ②初始化方法在对象的整个生命周期里只使用一次【注:初始化方法是在对象的初始化阶段完成其实例变量的赋值操作,一个对象的初始化阶段只有一次,所以初始化方法只使用一次】

    ③“-”实例方法

    - (instancetype)init{
        // self在实例方法中代表实例对象
        // self在类方法中代表类
        self = [super init];
        // 判断从父类继承过来的init方法是否初始化成功
        if (self != nil) {//if(self)  //if(self = [super init])  非零即为真
            // 初始化实例变量
            _name = @"大白";
        }
        // 返回初始化完成的对象
        return self;
    }

    <1>使用super调用父类的初始化方法,用于初始化继承自父类的公共实例变量
    <2>初始化完成之后会返回一个地址,这个地址就是对象的地址
    <3>self是一个指针,指向自己的对象。self保存返回的地址。再初始化自身特有变量。
    <4>返回值有可能为空。如果返回值为空,就什么也不做。返回值不为空,初始化自己的实例变量。

    5.指定初始化方法

    ①一个类可以有多个初始化方法

    ②虽然可以有多个初始化方法,但是一个对象只能使用一个初始化方法。

    ③通常会把在初始化时想做的操作全部放到指定初始化方法中

    ④选取原则:一般选参数最多的初始化方法作为指定初始化方法

    ⑤情况1:

    .h中声明

    - (instancetype)initWithName:(NSString *)name age:(NSInteger)age;

    .m中实现

    - (instancetype)initWithName:(NSString *)name age:(NSInteger)age{
        
        if (self = [super init]) {
            _name = name;
            _age = age;
        }
        return self;
    }

    ⑥情况2:

    .h声明

    - (instancetype)initWithName:(NSString *)name;
    
    - (instancetype)initWithName:(NSString *)name Age:(NSInteger)age;
    
    - (instancetype)initWithName:(NSString *)name Age:(NSInteger)age Score:(CGFloat)score;

    .m实现

    #import "Student.h"
    @implementation Student
    - (instancetype)initWithName:(NSString *)name Age:(NSInteger)age Score:(CGFloat)score{
        
        if (self = [super init]) {
            _name = name;
            _age = age;
            _score = score;
        }
        return self;
    }
    
    - (instancetype)initWithName:(NSString *)name Age:(NSInteger)age{
        //凡是基本数据类型填0,对象、类填nil
        return [self initWithName:name Age:age Score:0];
    }
    
    - (instancetype)initWithName:(NSString *)name{
        
        return [self initWithName:name Age:0 Score:0];
    }
    @end

    6.便利构造器

    ①内部实现:封装了alloc和初始化操作,创建对象更加方便

    ②“+”类方法

    ③返回本类型的实例

    ④方法名以类名开头

    ⑤可以有0到多个参数

    .声明

    + (instancetype)teacherWithName:(NSString *)name age:(NSInteger)age;

    .实现

    + (instancetype)teacherWithName:(NSString *)name age:(NSInteger)age{
        
        Teacher *t = [[Teacher alloc]initWithName:name age:age];
        return t;
    }

    .调用

    Teacher *t2 = [Teacher teacherWithName:@"Lee" age:13];

    三、属性和点语法

    1.属性

    ①提供setter、getter方法的默认实现

    ②关键字:

    @property(声明)

    @synthesize(实现)【可以省略,没有实现setter和getter方法时,通过该关键字自动生成】

    ③如果方法内部操作的实例变量未定义,系统会自动生成一个_属性名的实例变量,但是生成的实例变量的可见度是私有的,子类不可访问。

    ④一旦同时重写了setter、getter方法,并且没有实现@synthesize,@synthesize就不再生成实例变量。需要写@synthesize

    2.属性特性

    ①读写性

    readonly:只读状态(只生成getter方法)

    readwrite:读写状态(setter和getter方法都生成)【系统默认】

    setter=:指定属性生成的setter方法的名字

    getter=:指定属性生成的getter方法的名字

    ②原子性

    atomic:原子特性。setter、getter内部做了多线程处理。【系统默认】

    nonatomic:非原子特性,生成普通setter、getter方法。【通常使用】

    ③语义特性

    <1>MRC(手动引用计数)

    assgin:非对象类型(比如int、float)属性的语义设置

    @property (nonatomic, assign) NSInteger age;
    setter⽅方法内部实现:
       - (void)setAge:(NSInteger)age {
         _age = age;
    }
    getter⽅方法内部实现:
       - (NSInteger)age {
         return _age;
    }

    retain:对象类型(比如:NSString,NSMutableDictionary等)属性的语义设置

    @property (nonatomic, retain) NSString *name;
    setter⽅方法内部实现:
    - (void)setName:(NSString *)name {
      if (_name != name) {
          [_name release];
          _name = [name retain];
      }
    }
    getter⽅方法内部实现:
    - (NSString *)name {
      return  [[_name retain] autorelease];
    }

    copy:对象类型并且想得到对象的副本(NSString)

    @property (nonatomic, copy) NSString *gender;
    setter内部实现
     - (void)setGender:(NSString *)gender {
       if (_gender != gender) {
           [_gender release];
           _gender = [gender copy];
       }
    }
    getter内部实现
     - (NSString *)gender {
       return  [[_gender retain] autorelease];
    }

    跟retain不同,一个对象想要copy,生成自己的副本,需要服从

    <2>ARC(自动引用计数)

    assign:修饰基本数据类型

    weak:修饰类对象

    strong:修饰类对象

    copy:修饰类对象

    四、OC字符串和数值

    1.API文档

    ①Xcode -> Help ->Documentation and API Reference

    Inherits form 继承关系

    Conforms to 遵循什么协议

    framework 属于哪个框架

    Availability 什么时候可以用

    Declared in 声明在什么头文件里

    Related documents 相关文档

    Sample code 示例代码

    ②option+鼠标左键

    ③command+鼠标左键

    2.字符串(NSString和NSMutableString)

    ①OC中字符串由unichar(unicode)组成

    ②NSString字符串(不可变)

    <1>创建

         // 创建字符串(错误)
        // NSString是不可变字符串,意味着创建之后不能改变,使用init完成之后字符串就创建完了,只得到一个空字符串。
        NSString *s2 = [[NSString alloc] init];
        
        // 正确创建字符串
        // 格式化初始字符串
        NSString *s3 = [[NSString alloc] initWithFormat:@"Lanou%@",s1];
        //便利构造器
        NSString *s4 = [NSString stringWithFormat:@"Lanou%@",s1];
        // 字面量
        NSString *s5 = @"hello";

    <2>常用方法

        NSString *str1 = @"abcdefg";
        // 获取字符串长度
        NSLog(@"%ld",[str1 length]);
        
        // 获取指定位置的字符(不推荐)
        unichar c = [str1 characterAtIndex:4];
    
        // 判断字符串是否相等
        BOOL b = [str1 isEqualToString:@"abcdefg"];
    
        // 比较两个字符串大小
        NSComparisonResult result = [str1 compare:@"bbb"];
        
        // 截取子串
        // 从哪开始截取
        NSString *str2 = [str1 substringFromIndex:4];
        
        // 从开始到哪结束
        NSString *str3 = [str1 substringToIndex:2];
        
        // 从哪开始截取,到哪里结束(获取单个字符使用这个方法)
    //    NSRange r = {0,3};
        NSString *str4 = [str1 substringWithRange:NSMakeRange(2, 1)];
        
        // 拼接字符串
        NSString *str5 = [str1 stringByAppendingFormat:@"hi"];
        
        // 替换字符串
        NSString *str6 = [str1 stringByReplacingCharactersInRange:NSMakeRange(1, 6) withString:@"xxx"];
        
        // 字符串对象转化为int型
        // 注意:不要在数字中加入字符,否则字符在哪就在哪停止
        NSString *str7 = @"123";
        NSInteger i = [str7 intValue];
    
        // int转为字符串
          NSString *str1 = [NSString stringWithFormat:@"%d",number];
    
        // 字符串全部大写
        NSString *str8 = [str1 uppercaseString];
        
        // 字符串全部小写
        NSString *str9 = [str1 lowercaseString];
        
        // 字符串首字母大写
        NSString *str10 = [str1 capitalizedString];
        
        // 判断后缀(重点)
        // 后缀:以字符串最后一个字符结尾的子串
        // 前缀:以首字母开头的子串
        
        // 是否以指定字符串为后缀
        BOOL b2 = [str1 hasSuffix:@"g"];
        
        // 是否以指定字符串为前缀
        BOOL b3 = [str1 hasPrefix:@"a"];

    ②NSMutableString(动态可变字符串)

    <1>创建

    Capacity参数值为预估的空间大小,但是会根据实际的存储情况,动态的调整实际空间大小【翻倍】

    初始化方法
        NSMutableString *strm1 = [[NSMutableString alloc] initWithCapacity:100];

    <2>常用方法

    // 拼接字符串
        [strm1 appendFormat:@"123"];
    
    // 在哪里插入字符串
        [strm1 insertString:@"000" atIndex:1];
    
    // 删除字符串
        [strm1 deleteCharactersInRange:NSMakeRange(1, 3)];
    
    // 替换字符串
        [strm1 replaceCharactersInRange:NSMakeRange(1, 1) withString:@"5"];
    
    // 重置字符串
        [strm1 setString:@"abc"];

     3.数值类(NSNumber)

    ①作用:实现基本数据类型与OC对象类型的相互转化

    ②基本数据类型(int,float等)转换为NSNumber

    // int类型转化为NSNumber类型的对象
        + (NSNumber *)numberWithInt:(int)value;
    // float类型转化为NSNumber类型的对象
        + (NSNumber *)numberWithFloat:(float)value;
    // char类型转化为NSNumber类型的对象
        + (NSNumber *)numberWithChar:(char)value;

    ③NSNumber转换为基本数据类型(int,float等)

    // NSNumber类型对象转化为int类型的数据
        @property (readonly) int intValue;
    // NSNumber类型对象转化为float类型的数据
        @property (readonly) float floatValue;
    // NSNumber类型对象转化为char类型的数据
        @property (readonly) char charValue;

    ④常用方法

    // NSNumber类型对象的⽐比较
    - (NSComparisonResult)compare:(NSNumber *)otherNumber;

    ⑤字面量

    // 常量:
        NSNumber *intNumber = @38;
        NSNumber *charNumber = @‘w';
    // 变量:
        int age = 18;
        NSNumber *ageNumber = @(age);
        char gender = 'w';
        NSNumber *genderNumber = @(gender);

    4.NSValue(完成结构体和对象类型的转换)

    ①结构体(NSRange等)转换为NSValue

    ②NSValue转换为结构体(NSRange等)

    ③常用方法

    // NSRange类型转化为NSValue类型的对象
    + (NSValue *)valueWithRange:(NSRange)range;
    // NSValue类型转化为NSRange类型的结构体变量
    @property (readonly) NSRange rangeValue;

    五、集合

    1.数组类(NSArray和NSMutableArray)

    ①只能存储对象类型,但是对于对象的类型没有限制【有序的集合】

    ②NSArray(不可变数组)

    <1>创建

    // 注意:nil作为数组的结束标志,不要手动添加。
    // 初始化方法
        NSArray *arr1 = [[NSArray alloc] initWithObjects:@"a",@"b",@"c", nil];
    // 便利构造器
        NSArray *arr2 = [NSArray arrayWithObjects:@"d",@"e",@"f",@"g",nil];
    // 字面量(结尾不需要加nil)
        NSArray *arr3 = @[@"mike",@"Joe",@"kitty"];

    <2>常用方法

    // 获取元素个数
        NSUInteger c1 = arr1.count;//[arr1 count]
    
    // 获取数组中指定下标对应的元素
        NSString *obj1 = [arr3 objectAtIndex:0];
    
    // 字面量获取元素
        NSLog(@"%@",arr3[2]);
    
    // 判断数组中是否包含某个元素
        BOOL b1 = [arr1 containsObject:@"m"];
    
    // 返回一个元素在数组中的索引
        NSUInteger n1 = [arr1 indexOfObject:@"b"];
    
    // 分割字符串
        NSString *str1 = @"www.lanou3g.com";
        NSArray *arr4 = [str1 componentsSeparatedByString:@"."];
    
    // 拼接字符串
        NSString *str2 = [arr4 componentsJoinedByString:@"/"];
        NSLog(@"%@",str2);

    ③NSMutableArray(可变数组)

    <1>创建

    // 初始化方法
         NSMutableArray*marr1 = [[NSMutableArray alloc]initWithCapacity:10];
    
    // 便利构造器
        NSMutableArray *marry2 = [NSMutableArray arrayWithCapacity:10];
    
    // 可变数组使用字面量(字面量创建的数组是不可变的)
        NSMutableArray *marr3 =@[@"a",@"b",@"c"].mutableCopy
    //[@[@"a",@"b",@"c"] mutableCopy]点语法getter

    <2>常用方法

    // 添加元素
        [marr1 addObject:@"a"];
    
    // 插入元素
        [marr1 insertObject:@"x" atIndex:2];
    
    // 删除元素
        //按元素删
        [marr1 removeObject:@"x"];
        //按位置删
        [marr1 removeObjectAtIndex:0];
        // 删除最后一个
        [marr1 removeLastObject];
        // 删除全部
        [marr1 removeAllObjects];
    
    // 使用指定元素替换指定位置上的元素
        [marr4 replaceObjectAtIndex:marr4.count - 1 withObject:@"e"];
    
    // 交换两个指定位置的元素
        [marr4 exchangeObjectAtIndex:0 withObjectAtIndex:3];

    2.字典类(NSDictionary和NSMutableDictionary)

    ①用来存储一一对应关系的数据【无序的集合】

    ②key和value必须是对象类型,每一对称为一个条目

    ③靠key存取元素

    ④NSDictionary(不可变字典)

    <1>创建

    // 初始化方法
         NSDictionary*dict1 = [[NSDictionaryalloc]initWithObjectsAndKeys:@"Mike",@"M",@"Lee",@"L",@"Kitty",@"K",nil];
    
    // 字面量
        NSDictionary *dict2 = @{@"M":@"Mike",@"L":@"Lee",@"K":@"Kitty"};

    <2>常用方法

    // 获取键值对个数
        NSUInteger c1 = dict1.count;//[dict1 count]
    
    // 获取字典中所有的key值
        NSArray *arr1 = dict1.allKeys;//[dict1 allKeys]
    
    // 获取字典中所有的value
        NSArray *arr2 = dict1.allValues;//[dict1 allValues]
    
    // 根据key获得对应的value
        NSString *obj1 = [dict1 objectForKey:@"M"];
    
    // 字面量获取
        NSString *obj2 = dict1[@"L"];
    
    //注意:在字典中key不能重复
        NSDictionary *dict3 =@{@"M":@"Mike",@"L":@"Lee",@"T":@"Tim",@"L":@"Lucy"};

    ⑤NSMutableDictionary(不可变数组)

    <1>创建

    // 初始化方法
        NSMutableDictionary *mdict1 = [[NSMutableDictionary alloc] initWithCapacity:10];
    
    // 字面量
        NSMutableDictionary *mdict2 = @{@"M":@"Mike",@"L":@"Lee"}.mutableCopy;

    <2>常用方法

    // 添加(如果字典中没有给定的key,就添加这个键值对)
        [mdict2 setValue:@"Tim" forKey:@"T"];
    
    // 修改(如果字典中有给定的key,直接修改value)
        [mdict2 setObject:@"Lucy" forKey:@"L"];
    
    // 给定key删除
        [mdict2 removeObjectForKey:@"M"];
    
    // 删除所有键值对
        [mdict2 removeAllObjects];

     3.集合类(NSSet和NSMutableSet)

    ①互异性、无序性,经常用来处理重用问题

    ②NSSet(不可变集合)

    <1>创建

    // 初始化⽅方法
    NSSet *name = [[NSSet alloc] initWithObjects:@"frank", @"duck", @"monkey",nil];
    
    // 便利构造器
     NSSet *name = [NSSet setWithObjects:@"frank",@"duck", @"monkey", nil];

    <2>常用方法

    // 元素个数
        NSLog(@"%ld",set.count);
    
    // 将set中的所有元素放到数组中
        NSArray *setArr = set.allObjects;
    
    // 任意取出一个元素
        NSString *s1 = [set anyObject];
    
    // 判断set中是否包含给定对象
        BOOL b = [set containsObject:@"6"];

    ③NSMutableSet(可变集合)

    <1>创建

    // 初始化⽅方法
    NSMutableSet *name = [[NSMutableSet alloc] initWithCapacity:0];
    
    // 便利构造器
    NSMutableSet *name = [NSMutableSet setWithCapacity:0];

    <2>常用方法

    // 添加⼀一个对象
       - (void)addObject:(id)object;
    // 移除⼀一个对象
       - (void)removeObject:(id)object;
    // 移除所有对象
      - (void)removeAllObjects;

    六、集合遍历和数组排序

    1.for循环遍历

    ①原理:通过for循环的循环变量用作数组元素下标来获取不同下标的元素

    ②循环次数就是数组元素的个数

    <1>遍历数组

        NSArray *arr = @[@"Mike",@"Lee",@"Kitty"];
        for (int i = 0 ; i < arr.count ; i ++) {
            NSLog(@"%@",arr[i]);
        }

    <2>遍历字典

        NSDictionary *dict = @{@"M":@"Mike",@"L":@"Lee",@"K":@"Kitty"};
        // 获取key
        NSArray *keyArr = dict.allKeys;
        // 遍历数组
        for (int i = 0 ; i < keyArr.count; i ++) {
            NSLog(@"%@",dict[keyArr[i]]);//[dict objectForKey:keyArr[i]]
        }

    <3>遍历集

        NSSet *set = [NSSet setWithObjects:@"1",@"2",@"3", nil];
        // 将集合中的元素放到数组中
        NSArray *allArr = set.allObjects;
        for (int i = 0 ; i < allArr.count; i ++) {
            NSLog(@"%@",allArr[i]);
        }

    2.枚举器(NSEnumeration)

    ①遍历集合中的元素

    ②依附于集合类,没有用来创建实例的接口

    ③对可变集合进行枚举操作,不能通过添加或删除对象这类方式改变集合容器的元素个数

    ④注意:由于字典和集合中存储的元素是无序的,因此没有反向枚举的概念

    <1>数组

        NSArray *arr2 = @[@"1",@"2",@"3"];
        // 创建枚举器(必须依靠一个容器对象)
        // 正向
        NSEnumerator *enum1 = [arr2 objectEnumerator];
        // 创建一个保存取出元素的变量
        id value1 = nil;
        // 循环取值
        while (value1 = [enum1 nextObject]) {
            NSLog(@"%@",value1);
        }
    
        // 逆向
        NSEnumerator *enum2 = arr2.reverseObjectEnumerator;
        // 创建一个保存取出元素的变量
        id value2 = nil;
        // 循环取值
        while (value2 = [enum2 nextObject]) {
            NSLog(@"%@",value2);
        }

    <2>字典

        NSDictionary *dict2 = @{@"M":@"Mike",@"L":@"Lee",@"K":@"Kitty"};
        // 创建枚举器
        NSEnumerator *enum3 = [dict2 objectEnumerator];
        // 创建一个保存取出元素的变量
        id value3 = nil;
        // 循环变量
        while (value3 = [enum3 nextObject]) {
            NSLog(@"%@",value3);
        }

    <3>集

        NSSet *set2 = [NSSet setWithObjects:@"1",@"2",@"3", nil];
        // 创建枚举器
        NSEnumerator *enum4 = [set2 objectEnumerator];
        // 创建一个保存取出元素的变量
        id value4 = nil;
        // 循环变量
        while (value4 = [enum4 nextObject]) {
            NSLog(@"%@",value4);
        }

    3.for...in

    ①对可变集合进行快速枚举操作时,不能通过添加或删除对象这类方式来改变集合容器的元素个数

    <1>数组

        NSArray *arr3 = @[@"1",@"2",@"3"];
        for (NSString *str in arr3) {
            NSLog(@"%@",str);
        }

    <2>字典

        NSDictionary *dict3 = @{@"M":@"Mike",@"L":@"Lee",@"K":@"Kitty"};
        // 取出key
        for (NSString *s in dict3) {
            NSLog(@"%@",s);
        }
        // 取出value
        for (NSString *s1 in dict3) {
            NSLog(@"%@",dict3[s1]);
        }

    <3>集

        NSSet *set3 = [NSSet setWithObjects:@"1",@"2",@"3", nil];
        for (NSString *s1 in set3) {
            NSLog(@"%@",s1);
        }

    4.数组排序

    ①数组是有序容器,因此集合中只有数组才能排序

    ②NSSortDescriptor

    // 初始化方法
         - (instancetype)initWithKey:(NSString *)key  ascending:(BOOL)ascending;
    // 数组根据排序条件进⾏行排序,得到排好序的新的数组对象。
         - (NSArray*)sortedArrayUsingDescriptors:(NSArray *)sortDescriptors;
    sortDescriptors:数组类型的对象,数组中可以存放多个排序描述对 象,最终能够实现按照多个要求进⾏行排序的⽬目的。
    ⽐如:如果该参数中存储了由name和age两个Person属性参与创建 的NSSortDescriptor对象,在按name进⾏行排序的同时,如果遇到姓名相同的就会按照age进⾏行排序。
    <1>数组中存放自定义类的对象
    // 创建数组对象
    NSArray *array = @[@"zhegnzhou", @"beijing",@"shanghai", @"guangzhou", @"xian", @"dalian"];
    // 创建排序条件
    NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:@"self" ascending:YES];
    // 数组根据排序条件进⾏行排序
    NSArray *resultArray = [array sortedArrayUsingDescriptors:@[descriptor]];

    <2>数组中存放直接可进行排序的对象

    // 创建数组对象,数组中存储多个Person对象
    NSArray *array = @[per1, per2, per3, per4, per5];
    // 创建排序条件,按照Person对象的姓名降序排序
    NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:NO];
    // 数组根据排序条件进⾏行排序
    NSArray *resultArray = [array sortedArrayUsingDescriptors:@[descriptor]];

    ③其他数组排序方法

    <1>不可变数组排序(排序结果生成新数组,原数组无改变)

     - (NSArray *)sortedArrayUsingSelector:(SEL)comparator;
    // 注:SEL类型的参数comparator:需要传入一个返回结果是 NSComparisonResult的方法名。
    NSArray *array = @[@"lanou", @"zhengzhou", @“henan",@“huimian"];
    NSArray *newArray = [array sortedArrayUsingSelector:@selector(compare:)];

    数组array中的元素都是字符串。字符串比较的方法compare:返回值类型正好满足SEL参数的需求

    <2>可变数组排序(直接对原数组进行操作,无新数组生成)

         (void)sortUsingSelector:(SEL)comparator;
    // 注:SEL类型的参数comparator:需要传入一个返回结果是 NSComparisonResult的函数

    <3>数组中存放自定义的类的对象

    NSArray *personArray = @[p1,p2,p3,p4,p5];
    
    // 按姓名升序
    NSArray *newNameArrayAscending = [personArray sortedArrayUsingSelector:@selector(compareByNameAscending:)];
    
    // 按姓名降序
    NSArray *newNameArrayDescending = [personArray sortedArrayUsingSelector:@selector(compareByNameDescending:)];
    比较方法的声明
    // 按姓名升序
    - (NSComparisonResult)compareByNameAscending:(Person *)anotherPerson;
    
    // 按姓名降序
    - (NSComparisonResult)compareByNameDescending:(Person *)anotherPerson;
    比较方法的实现
    // 按姓名升序
    - (NSComparisonResult)compareByNameAscending:(Person *)anotherPerson{
    
        return [self.name compare:anotherPerson.name];
    
    }
    
    // 按姓名降序
    - (NSComparisonResult)compareByNameDescending:(Person *)anotherPerson{
    
        if (self.name < self.name) {
    
            return 1;
    
        }else{
    
            return 0;
    
        }
    
    }

    七、内存管理

    1.OC内存管理的机制(GC和RC)

    ①GC:垃圾回收机制(Garbage-Collection)

    程序员只需开辟内存空间,不需要用代码的形式释放,系统来判断哪些空间不再被使用,并回收这些内存空间,以便再次分配。整个回收的过程不需要写任何代码,由系统自动完成垃圾回收。Java开发中一直使用的就是垃圾回收技术。

    ②RC:引用计数机制(MRC和ARC)【iOS支持的两种内存管理方式】

    <1>MRC(Manual Reference Counting)人工引用计数【内存管理机制:引用计数】

    内存的开辟和释放都由程序代码进行控制。相对垃圾回收来说,对内存的控制更加灵活,可以在需要释放的时候及时释放。

    <2>ARC(Auto Reference Counting)自动引用计数【基于MRC】

    iOS 5.0的编译器特性,它允许用户开辟空间,不用去释放空间。它不是垃圾回收!它的本质还是MRC,只是编译器帮程序员默认加了释放的代码。

    2.引用计数机制【栈结构的先进后出】

    ①每个对象都有一个引用计数器,用来记录当前对象的引用次数

    ②当一个新的引用指向对象时,引用计数器就加1,当去掉一个引用时,引用计数就减1.当引用计数到零时,该对象的空间就被系统回收。

    ③retainCount获取对象的引用计数
    ④方法:

    <1>+alloc(生成对象)【分配内存并且将内存的引用计数置为1】

    <2>-retain(持有对象)【引用计数加1】【通常来说,一块内存有几个指针指向,引用计数就应该是几】

    <3>-copy【把某一对象的内容拷贝一份,拷贝出新的对象,原有对象的引用计数不变,新的对象的引用计数变1】

    <4>-release【引用计数立即减1】/-autorelease【未来的某一时刻引用计数减1】(释放对象)

    【-release:一个指针不再使用这块内存,就应该release。release完成之后,最好指针置为nil(空)】

    p1 = nil;// 改变P1指向

    【-autorelease:通过autoreleasepool自动释放池,控制autorelease对象的释放】

    <5>-dealloc(销毁对象)

    继承自父类的方法,当对象引用计数为0的时候,由对象自动调用,销毁该对象的空间;

    // 当对象释放前会执行该方法
    - (void)dealloc{
        NSLog(@"释放%@",_name);
        // 调用父类中的dealloc
        [super dealloc];// 父类对该方法的实现才是真正的回收空间
    }

    ⑤NSAutoreleasePool(自动释放池)

    <1>方式一:

    // 创建自动释放池
        NSAutoreleasePool *pool1 = [[NSAutoreleasePool alloc] init];
         代码内容;
    // 自动释放池销毁
        [pool1 release];

    <2>方式二:【自动释放池使用这种,更安全】

    @autoreleasepool { // 创建自动释放池} // 自动释放池销毁

    3.内存管理原则

    ①在一段代码内,增加和减少的次数要相等

    ②如果增加的次数大于减少的次数,会造成内存泄露

    ③如果增加的次数小于减少的次数,会造成内存过度释放

    ④如果增加的次数等于减少的次数,还继续访问,造成野指针问题

    4.协议(Protocol)【一个类可以签多个协议,用逗号隔开<,>】

    ①只有.h文件

    ②接受协议的类实现协议中定义的方法

    ③实现方法:command+n -> 选择os x下的source -> Objective-C File -> Next -> File Type(选择Protocol)

    ④结构

    @protocol MyProtocol <NSObject>
    //【NSObject可以选择性删除,继承于NSObject】
    @required// 方法必须实现(默认)
    - (void)eating;
    @optional// 可选实现的
    - (void)sayHi;
    @end

    5.拷贝

    ①copy方法

    <1>跟retain不同,一个对象想要copy,生成自己的副本,需要服从NSCopying协议,定义copy的细节(如何copy)。如果没有接受NSCopying协议而给对象发送copy消息,会引起crash

    <2>copy方法的实现

      <NSCopying>

    <3>copy方法的使用

    - (id)copyWithZone:(nullable NSZone *)zone{
    //伪拷贝:相当于retain【拷贝地址,引用计数加1】
        return [self retain];
    
    // 浅拷贝:只对对象进行开辟新空间,对象中的属性公用
    //【对象开辟新的空间,但是两个对象的实例变量指向同一块空间】
        Person *temp = [[Person allocWithZone:zone] init];
        temp.name = self.name;
        return temp;
    
    // 深拷贝:对对象开辟空间,对象中属性也开辟空间,然后内容全部复制过去
    //【对象开辟新的空间,两个对象的实例变量也指向不同的空间】
        Person *temp = [[Person allocWithZone:zone] init];
        temp.name = [[NSString alloc] initWithString:self.name];
        return temp;
    }

    <4>【不是任何对象都可以接收copy消息,只有接受了NSCopying协议的对象才能接收copy消息】

    八、高级内存管理

    1.属性的语义特性(setter和getter方法优化)

    ①assign(使用范围:基本数据类型:char,short,int,float,double)

         @property (nonatomic,assign)NSInteger name;
    // ①setter
    - (void)setName:(NSString *)name{
        _name = name;
    }
    // ②getter
    - (NSString *)name{
        return _name;
    }

    ②reetain(使用范围:对象类型)

         @property (nonatomic,retain)NSString *name;
    // ①setter
    - (void)setName:(NSString *)name{
        if (_name != name) {
            [_name release];
            _name = [name retain];
        }
    }
    // ②getter
    - (NSString *)name{
        // 防止野指针
        return [[_name retain] autorelease];
    }

    ③copy(对象类型,且遵守了<NSCopying>协议)

    【如果要对一个对象进行copy,那该对象所属的类必须遵守<NSCopying>协议】

         @property (nonatomic,copy)NSString *name;
    // ①setter
    - (void)setName:(NSString *)name{
    
        if (_name != name) {
            [_name release];
            _name = [name copy];
        }
    }
    // ②getter
    - (NSString *)name{
        // 防止野指针
        return [[_name retain] autorelease];
    }

    2.dealloc释放实例变量

    ①dealloc是NSObject的一个实例方法,用于回收alloc开辟的内存空间
    ②在对象引用计数为0是,由系统自动调用

    ③通常在dealloc中释放类的实例变量

    // 重写销毁
    - (void)dealloc{
        // 在对象销毁之前,将对象中的实例变量和属性销毁
        [_name release];
        [super dealloc];
    }

    ④注意:

    <1>永远不要手动调用dealloc

    <2>在dealloc方法的最后一行,必须要写[super dealloc],让系统真正的去销毁对象

    3.初始化方法【优化】

    // 初始化方法
    - (instancetype)initWithName:(NSString *)name{
        if (self = [super init]) {
            // 调用setter方法
            // 以后所有方法中给实例方法赋值,都使用setter方法【切记,很重要】
            self.name = name;// 如果不用,可能出现野指针问题
        }
        return self;
    }

    4.便利构造器的内存管理

    ①便利构造器,一定配套使用自动释放池

    ②所有使用便利构造器创建的对象都不需要释放

    + (instancetype)personWithName:(NSString *)name{
    
        Person *p =  [[Person alloc] initWithName:name];
        return  [p autorelease];
    }

    5.集合的内存管理

    ①常见的集合类有:NSArray,NSDictionary,NSSet
    ②集合会自主管理集合内部元素
    ③流程
    <1>加入集合的元素会被retain
    <2>移除集合的元素会被release
    <3>集合被释放时,会对集合中所有元素release
    6.KVC
    ①KVC:key Value Coding ,键值编码,是一种间接访问实例变量的方法。
    ②KVC提供了一种使用字符串(key)而不是访问器方法,去访问一个对象实例变量的机制
    ③KVC按顺序使用如下技术:
    <1>检查是否存在getter方法-<key>或者setter方法-set<key>:的方法
    <2>如果没有上述方法,则检查时候存在名字为-_<key>、<key>的实例变量
    <3>如果仍未找到,则调用valueForUndefinedKey:和setValue:forUndefinedKey:方法。这些方法的默认实现都是抛出异常,可以根据需要重写。
    ④常用方法
    <1>
    取值
    - (id)valueForKey:(NSString *)key;
    设置值
    - (void)setValue:(id)value forKey:(NSString *)key;
    以Person类为例
    @interface Person : NSObject
    @property (nonatomic, copy) NSString *name;
    @end
    
    // 属性
    Person *p = [[Person alloc] init]; p.name = @"张三";
    NSLog(@"%@", p.name);
    
    // KVC
    Person *p = [[Person alloc] init];
    [p setValue:@"张三" forKey:@"name"]; NSLog(@"%@", [p valueForKey:@"name"]);
    <2>
    获取键值路径
    - (id)valueForKeyPath:(NSString *)keyPath;
    设置键值路径
    - (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
    以Person类为例
    @interface Person : NSObject
    @property (nonatomic, copy) NSString *name;
    @property (nonatomic, copy) NSString *gender;
    @end
    
    // 属性
    Person *p = [[Person alloc] init]; p.name = @"张三";
    p.gender = @"";
    NSLog(@"%@ %@", p.name, p.gender);
    
    // KVC
    Person *p = [[Person alloc] init];
    NSDictionary *dic = @[@"name": @"张三", @"gender": @""];
    [p setValuesForKeysWithDictionary:dic]; NSLog(@"%@ %@", [p valueForKey:@"name"], [p valueForKey:@"gender"]);
    <3>
    模型类:写的属性必须和字典中key值是一样的【一模一样】
    - (void)setValuesForKeysWithDictionary:(NSDictionary*)keyedValues;
     1 Student.plist
    2 <?xml version="1.0" encoding="UTF-8"?> 3 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 4 <plist version="1.0"> 5 <array> 6 <dict> 7 <key>tel</key> 8 <string>11111</string> 9 <key>name</key> 10 <string>Mike</string> 11 <key>age</key> 12 <integer>20</integer> 13 <key>sex</key> 14 <string>男</string> 15 </dict> 16 <dict> 17 <key>tel</key> 18 <string>222222</string> 19 <key>name</key> 20 <string>Joe</string> 21 <key>age</key> 22 <integer>10</integer> 23 <key>sex</key> 24 <string>男</string> 25 </dict> 26 <dict> 27 <key>tel</key> 28 <string>333333</string> 29 <key>name</key> 30 <string>Kitty</string> 31 <key>age</key> 32 <integer>18</integer> 33 <key>sex</key> 34 <string>女</string> 35 </dict> 36 </array> 37 </plist>
     1 Student.m
     2 
     3 #import "Student.h"
     4 @implementation Student
     5 // 如果使用KVC赋值,一定重写这个方法。这个方法可以什么都不写。作用防止找不到对应的key而crash【崩溃】
     6 // 【在解析有大用途】
     7 - (void)setValue:(id)value forUndefinedKey:(NSString *)key{
     8 
     9     // NSLog(@"++=%@ : %@",key,value);
    10     
    11 }
    12 @end
    1 Student.h
    2 
    3 #import <Foundation/Foundation.h>
    4 // 模型类
    5 @interface Student : NSObject
    6 @property (nonatomic,copy)NSString *name;
    7 @property (nonatomic,retain)NSNumber *age;
    8 @property (nonatomic,copy)NSString *sex;
    9 @end
     1 main.m
     2 
     3 #import <Foundation/Foundation.h>
     4 #import "Student.h"
     5 int main(int argc, const char * argv[]) {
     6     
     7     NSArray *datArray = [NSArray arrayWithContentsOfFile:@"/Users/lanou3g/Desktop/OClesson8练习/OClesson8练习/Student.plist"];
     8     // 容器(保存对象的数组)
     9     NSMutableArray *arr = [NSMutableArray array];
    10     for (NSDictionary *dict in datArray) {
    11         Student *s = [[Student alloc] init];
    12         [s setValuesForKeysWithDictionary:dict];
    13         [arr addObject:s];
    14     }
    15     for (Student *stu in arr) {
    16         NSLog(@"%@ : %@ : %@",stu.name,stu.age,stu.sex);
    17     }
    18     
    19     return 0;
    20 }

    ⑤当使用KVC时,如果KVC值和属性名不一样时,在类中重写以下两个方法:

    【在使用KVC赋值时,一定要重写这个方法,这个方法可以什么都不写。作用防止找不到对应的key而崩溃】

    - (void)setValue:(id)value forUndefinedKey:(NSString *)key {
    }
    - (id)valueForUndefinedKey:(NSString *)key {
    }


    7.ARC

    ①ARC:Automatic Reference Counting,自动引用计数,由开发人员开辟内存空间,但是不需要释放该内存空间,由系统自动释放该空间
    ②ARC本质上还是基于MRC的,只不过是系统自动添加了释放内存的方法
    ③ARC是编译器特性
    ④从Xcode5.0后,创建的工程默认是开启ARC的【系统默认ARC】
    ⑤当工程开启ARC后,由于编译器会自动帮你释放内存,所有和内存相关操作retain、release、autorelease,都不能写。【copy可以用】
    ⑥重写dealloc方法时,也不能写[super dealloc]
    ⑦属性语义特性
    <1>assign:基本数据类型(char,short,int,float,double)
    <2>strong:对象类型,相当于MRC中的retain
    <3>copy:对象类型,且遵守了<NSCopying>协议
    <4>weak:对象类型,但是内部不会对对象做retain操作
    ⑧ARC和MRC混编
    如果需要对特定文件开启或关闭ARC,可以在工程选项中选择:
    Targets -> Compile Phases ->Compile Spurces
    在里面找到对应文件,添加flat
    <1>打开ARC:-fobjc-arc
    <2>关闭ARC:-fno-objc-arc
     
    九、类的扩展
    1.对比:
    ①子类化:创建一个子类直接继承自原有类,在该类中扩充新的功能,该方式既可以扩充方法,也可以扩充实例变量。但是,想使用扩充的功能,必须使用子类的对象,原有类的对象无扩充功能。
    ②修改源代码:这是一种最直接的添加功能的方式,但是局限于必须要拥有该类的源代码,我们才有修改的权限。该方式既可以扩充方法,也可以扩充实例变量。
    ③协议:这是一种间接扩充功能的方式,协议中只有一堆方法的声明,使用时需要类服从协议,实现协议中的方法来扩充功能,而且只能扩充方法,不能扩充实例变量。局限于必须要拥有该类的源代码。
    2.Category(分类、类目)
    ①也叫分类,类目。是为没有源代码的类扩充功能
    ②扩充的功能会成为原有类的一部分,可以通过原有类或者原有类的对象直接调用,并且可继承
    ③该方法只能扩充方法,不能扩充实例变量
    ④创建方式
    <1>command +n 选择Objective-C File
    <2>在File Type中选择文件类型:Category
    <3>文件扩展类型里选择要写类目的类名
    ⑤使用:
    <1>在类目的.h文件中声明需要添加的方法
    [1]为没有源码的类添加方法【不能添加实例变量】
    [2]可以将类的方法按功能分类【添加的方法名不要和已有的方法名重名】
    @interface NSString (AddMethod) //声明sayHi⽅方法
    
    - (void)sayHi;
    @end
    <2>在类目的.m文件中实现对应的方法
    @implementation NSString (AddMethod) //实现sayHi⽅方法
    - (void)sayHi {
    NSLog(@"我是⽜牛逼的字符串"); }
    @end
    <3>min中使用
    [1]使用类目添加的方法首先需要在对应的类中导入类目的.h文件
    #import <Foundation/Foundation.h>
    #import "NSString+AddMethod.h"
    int main(int argc, const char * argv[]) {
        NSString *str = [[NSString alloc] init];
        [str sayHi];
         return 0;
    }

    3.Extension(延展)

    ①私有变量(方法):类外部不能直接访问,甚至其子类也不能够直接访问
    ②使用场景:
    <1>为能够获得源代码的类添加私有的实例变量和方法
    <2>注意:延展操作的类必须是能够获得源代码的类(具有.m文件的类)
    ③通过延展定义的方法属于私有方法,外界是没有访问权限的,只能在当前类的.m文件中访问。
    ④格式:把代码写到原始类的.m中
    ⑤Teacher类中.m文件中的代码
    <1>延展:为一个已有源代码的类添加私有实例变量和管理私有方法声明
    @interface Teacher()
    {
    NSInteger _salary; //存储对应的薪资
    }
    - (NSInteger)getSalary; //声明⼀一个领⼯工资的⽅方
    @end
    
    @implementation Teacher
    //实现领⼯工资的操作
    - (NSInteger)getSalary {
    NSLog(@"这个⽉月发了不少,可以吃⼤大餐了"); _salary = 1000;
    
    return _salary;
    
    }
    @end

    4.delegate(代理)设计模式

    ①要素:委托方,代理方,协议
    <1>委托方:委托别人去执行某些操作的人(对象)
    <2>协议(Protocol):委托方需要代理方执行的操作
    <3>代理方:被委托去执行某些操作的人(对象)
    ②协议(Protocol)是默认实现的,即@required;@optional修饰的方法是可选的,可实现也可不实现
    ③设计步骤:
    <1>委托方指定一套协议(在委托方的.h文件中),协议中声明委托方需要让代理方执行的方法(只有方法声明)
    <2>委托方声明一个delegate属性(assign修饰),存储代理方对象
    <3>代理方需要遵守协议,并且对协议中的方法进行实现
    <4>将代理方设置为委托方的代理人(将代理方对象赋值给委托方对象的delegate属性,进行存储)
    <5>委托方在合适的时机通知代理方对象去执行相应的操作
    ④respondsToSelector:该方法判断对象是否能够对某一个选定的方法做出响应。
    ①Girl.h
    #import <Foundation/Foundation.h>
    
    // 协议
    
    @protocol MyProtocol <NSObject>
    
    // 触发了某些事情
    
    // 吃东西
    - (void)eating;
    
    // 喝东西
    - (void)drinking;
    
    // 逛街
    - (void)shopping;
    
    @end // <MyProtocol>
    
    @interface Girl : NSObject
    
    @property (nonatomic,copy)NSString *name;
    
    // 准备代理属性(weak弱引用,有关系但不持有)【必须使用weak】
    
    // 代理必须标明协议
    
    @property (nonatomic,weak)id<MyProtocol> delegate;
    
    // 女孩饿了
    - (void)hungry;
    
    // 女孩渴了
    - (void)thirsty;
    
    // 女孩不开心了
    - (void)unHappy;
    @end //Girl
    ②Girl.m
    #import "Girl.h"
    
    @implementation Girl
    
    // 女孩饿了
    - (void)hungry{
        // 代理调用吃东西的方法
        // 使用代理需要做判断(是否有代理和是否有该方法)
        if (_delegate != nil && [_delegate respondsToSelector:@selector(eating)]) {
            [_delegate eating];
        }else{
            NSLog(@"自己做饭吃");
        }
    }
    
    // 女孩渴了
    - (void)thirsty{
        // 代理调用喝东西的方法
        if (_delegate != nil && [_delegate respondsToSelector:@selector(drinking)]) {
            [_delegate drinking];
        }else{
            NSLog(@"自己喝水");
        }
    }
    
    // 女孩不开心了
    - (void)unHappy{
        if (_delegate != nil && [_delegate respondsToSelector:@selector(shopping)]) {
            [_delegate shopping];
        }else{
            NSLog(@"自己逗自己开心");
        }
    }
    @end
    
    ③Boy.h
    #import <Foundation/Foundation.h>
    #import "Girl.h"
    
    @interface Boy : NSObject<MyProtocol>
    
    @end// Boy
    
    ④Boy.m
    #import "Boy.h"
    
    @implementation Boy
    
    - (void)eating{
        NSLog(@"男孩去买吃的");
    }
    
    - (void)drinking{
        NSLog(@"男孩去买水");
    }
    
    - (void)shopping{
        NSLog(@"男孩陪女孩逛街");
    }
    
    @end// Boy
    
    ⑤main.m
    #import <Foundation/Foundation.h>
    #import "Girl.h"
    #import "Boy.h"
    
    int main(int argc, const char * argv[]) {
        Girl *g = [[Girl alloc] init];
        Boy *b = [[Boy alloc] init];
    
        // 设置代理
        g.delegate = b;
    
        [g hungry];
        [g thirsty];
        [g unHappy];
        return 0;
    }
  • 相关阅读:
    19. 删除链表的倒数第 N 个结点
    相交链表
    环形链表2
    环形链表
    K8s 网络通讯
    Hutool-二维码生成
    Hutool-加解密
    Hutool-解析JSON
    Hutool-读取配置文件中的配置
    Hutool-操作图片
  • 原文地址:https://www.cnblogs.com/gnhxsk/p/5229362.html
Copyright © 2011-2022 走看看