一、类和对象
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;
// 创建数组对象 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中释放类的实例变量
// 重写销毁 - (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.集合的内存管理
取值 - (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"]);
获取键值路径 - (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"]);
模型类:写的属性必须和字典中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
<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(延展)
@interface Teacher() { NSInteger _salary; //存储对应的薪资 } - (NSInteger)getSalary; //声明⼀一个领⼯工资的⽅方 @end @implementation Teacher //实现领⼯工资的操作 - (NSInteger)getSalary { NSLog(@"这个⽉月发了不少,可以吃⼤大餐了"); _salary = 1000; return _salary; } @end
4.delegate(代理)设计模式
①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; }