OC (一)
概括
OC概述
面向对象编程
类和对象
实例变量操作
类:一组具有相同特征和行为的事物的抽象集合。
对象:类中具体的。对象是类的实例。
类和对象是面向对象的核心。
三大步骤:定义类 ,创建对象,使用对象。
OC是一种能够完成复杂面向对象编程的简单的计算机设计语言。两种编程思想
面向过程编程(POP, Procedure Oriented Programming):分析解决问题的步骤,实现函数,依次调用。以过程为中心
面向对象编程(OOP, Object Oriented Programming):分析问题组成的对象,协调对象间的联系和通信,解决问题。以对象为中心
对象定义了解决问题的步骤中的行为,不刻意完成一个步骤。
OC中类的定义
面向对象编程中使用对象完成程序
开发中:先定义类 ,在创建对象,然后使用对象。
定义类包含两部分“:接口部分和实现部分。分开写。
1.接口部分:对外声明类的特征和行为。
2.实现部分:对内实现行为。
接口部分标志:@interface ...@end
作用: 声明类的实例变量和方法,即特征和方法
包含内容:类名 父类名 实例变量 方法。
实现部分标志:@implemention…@end文件:.h称为接口文件或者头文件 .m称为实现文件。
创建对象
创建对象分两步:分配内存空间和初始化
分配内存空间:根据类中声明的实例变量为对象分配内存,将所有的实例变量置为默认值0,并返回首地址
初始化:
为对象的实例变量设置初始值。
使用对象
指针存储对象的首地址,代指对象。OC中使用指针代指对象,进行操作。
Person *p =[Person alloc];先执行”=“ 右侧
1.[ person alloc] 返回值是对象的首地址,即对象。
2.p是跟对象同类型的指针变量,存储对象首地址,代指对象。并不是真正的对象
OC (二)
可见度 | 特点 |
public(公有的) | 实例变量可以被任意操作 |
protected(受保护的,默认的) | 实例变量只能在该类和其子类内操作 |
private(私有的) | 实例变量只能在当前类中操作 |
方法分类:
OC中的方法分两种:类方法和实例方法。
类方法:只能类使用,例如: +(id)alloc 注:类方法中不能使用实例变量
实例方法“只能对象使用。例如:- (void)sayHi
方法声明:方法名怎么找?
删除:类型标识,返回类型,参数类型,参数名,空格。 例如:
replaceObjectAtIndex:withObject:
类中不能出现同名方法。
“:”标识参数,不能省略。有冒号必须有参数。
在oc中没有person调用sayHi这种表述
在oc中使用消息发送机制 :[接受者 消息]
正确表述 :给person对象发送sayHi消息
①person接收到消息。即方法sayHi
②person找到sayHi方法,并执行。
当一个类中定义的实例变量的可见度为protected或者private时,这些实例变量无法通过实例对象名加指向操作符再加实例变量名的形式直接访问,这个时候可以为该类定义赋值方法和取值方法实现间接访问。
eg:实例变量“color”访问类的方法。
- (void)setColor:(NSString *)color {//当执行该方法时,color作为形参,会拷贝实参的内容,并且将拷贝到的内容赋值给对应需要操作的实例变量。
_color = color;
}
#import
导入头文件,即:导入头文件中的内容到当前类
#import“”导入自定义类#import<>导入类库中的头文件。
功能类似C语言中的#include,但是可以避免头文件被重复导入。
容易出现循环导入的问题。
解决出现循环导入的问题:
@class
告诉编译器@class 后的字符串作为类名使用,并未导入类的接口内容。
有效避免嵌套循环导入。
实例变量有3种常⻅见的可⻅见度:@public、@protected、@private。
@public违背了封装特性,⾯面向对象开发中很少使⽤用;@protected默认可见度,⾃己和子类中能使⽤->访问实例变量;@private⾃己类 中能使用->访问实例变量。
方法是OC的核心,采⽤用消息机制:[receiver message]。 “-”message由对象来调⽤用;“+”message由类来调⽤用。
OC (三)
概括
继承 初始化方法
继承
继承的上层: 父类 ,继承的下层:子类
继承是单向的,不能相互继承。
继承具有传递性:A继承于B,B继承于C,A具有B和C的特征和行为
子类能继承父类全部的特征和行为.
面向对象提供了的继承语法,能够大大简化代码,把公共的方法和实例变量写在父类里,子类只需要写自己独有的实例变量和方法即可
继承既能保证类的完整,又能简化代码,让类与类之间的关系更加紧密。
继承特点
OC中只允许单继承
没有父类的类称为根类,OC中的根类是NSObject(祖宗)。
继承的内容:所有实例变量和⽅方法。
如果⼦类不满意父类方法的实现,可以重写父类的方法。
- (void)eat{
NSLog(@"%@在吃草",_name);
}
eat方法是由子类继承自父类Person中的,如果自己需要修改父类中的实现,则只需在类的实现部分重新定义该方法即可。
super 是编译器指令,并非对象。 super是调用父类方法的实现
作用:给super发消息,是调用父类方法的实现。
当子类重写父类方法时,通过super执行父类的实现,又拥有自己的实现,相当于对父类已经定义的方法做扩展。
子类可以重写父类的方法,即子类既有自己的实现,又有父类继承下来的实现,如果想使用父类的实现,向super发送消息。
初始化方法
创建对象分两步:开辟空间,初始化。
初始化方法的主要作用是:为某些实例变量赋初值。
初始化方法在对象的整个生命周期里只使用一次。
学习继承之前,初始化方法,没有self=[super init] 字样。我们为 实例变量逐一赋值。
学习继承之后,父类生命了公共的实例变量。作为父类,也应该有自己的初始化方法,为这些公共实例变量赋初值。
子类定义了除父类中公共实例变量之外的实例变量。在自身的初始化方法中,优先向super发送init消息,初始化公共变量,初始化成功之后,再初始化自身特有变量,从而完成全部实例变量的初始化。初始化方法的特征
初始化方法是”-“方法
以init开头
可以带0到多个参数
内部实现:先执行super的初始化方法,在初始化自身变量,return self。
便利构造器
便利构造器在初始化的方法的基础上前进了一小步,封装了对象创建过程。
便利构造器是”+“方法,返回本类型的实例,方法名以类名开头。
可以有0到多个参数。
内部实现:封装了alloc和初始化方法。使用起来更加简洁。
实现:1. 声明和实现便利构造器。
2. 使⽤用便利构造器创建对象。
总结:
继承是面向对象三大特性之一,合理的继承,能减少很多冗余代码, 加快开发速度。
初始化⽅法以init开头,在对象的声明周期中只使⽤一次。 便利构造器封装了对象的创建过程,进一步简化了对象创建的步骤 .
OC (四)使用苹果帮助文档(API文档)NSString 和 NSMutableString, NSArray和 NSSMutableArray,NSNumber和Dictionary 和Set
// 搜索字符串baidu在http://www. baidu.com字符串中的范围
//NSStringFromRange函数可以将NSRange类型的结构体变量转换成字符串对象,方便输出打印。
// 截取子字符串(按照指定的范围截取)
NSRange range1 = NSMakeRange(10, 7);
//替换字符串操作
//通过新的字符串来替换已有的字符串 来得到最终的字符串对象作为返回值返回。
NSString *lowerCaseStr = [upperCaseStr lowercaseString];
可变字符串类
NSString
NSMutableString 是NSString的子类,继承得到的所有方法。
不可变字符串的修改操作,本质就是得到一个新的字符串。
不可变字符串的修改方法以String开头,有返回值。
可变字符串的修改方法没有返回值。修改原字符串。
数组对象的特点:
元素是有序的,索引之从0开始。
数组中存储的元素必须是对象。类型任意。
@120;//返回的是对象 NSNumber 类型
@"120";//返回的是对象,NSString 类型
//1.定义基本数据类型变量
char a ='f';
int b =120;
//2.将基本数据类型转为NSNumber类型的对象
NSNumber *a1 = [NSNumbernumberWithChar:a];
NSLog(@"%@", a1);
NSNumber *a2 = [NSNumbernumberWithInt:b];
NSLog(@"%@", a2);
//3.将NSNumber类型的对象转为基本数据类型
char a3 = [a1charValue];
NSLog(@"%c", a3);
int a4 = [a2intValue];
NSLog(@"%d", a4);
NSInteger a3 = [a2integerValue];
NSLog(@"%ld", a3);
//4.NSNumber 类型对象的比较
NSLog(@"%ld",[a1compare:a2]);
字典类
/*字典存在的价值 :
0.字典类是用于保存具有映射关系(key - value对)的数据,字典一旦创建,键值对就不可更改, 不可添加,不可删除.仅能读取key 或者 value
1. 大容器,用来存储多个数据,
2.用来存储的数据具有--对应的关系.(使用key来标识 value).
3.对于字典中的一对键值对(key - value)叫做字典中的一个元素.也叫一个条目, 只要是对象就可以, 不限制类型.
4.字典是无序的
5.字典中的key是唯一的,一个key 只能对应一个value ,一个value可以对应多个 key.
*/
1.初始化方法
NSDictionary *dic1 = [[NSDictionary alloc] initWithObjectsAndKeys:@"Summer", @"name", @"male", @"gender", @20, @"height", @12, @"age", nil];
NSLog(@"%@", dic1);
2.求字典元素个数
NSInteger count = [dic count];
NSLog(@"count = %ld", count);
3.获取所有的key
NSArray *keys = [dic allKeys];
NSLog(@"%@", keys);
4获取所有的value
NSArray *value = [dic allValues];
NSLog(@"%@", value);
5.根据key获取对应的value
//通过为对象字典发送objeForKey消息可以根据给定的key去获取对应的value,这里如果给定的key不存在,则返回null作为value,表示没有找到对应的value.
NSString *name = [dicobjectForKey:@"name"];
NSLog(@"name = %@", name);
NSNumber *age = [dicobjectForKey:@"age1"];
NSLog(@"age = %@", age);
6.字典快速遍历
//快速遍历:对于数组来说,遍历得到的是数组中的每一个元素
//对于字典来说,遍历得到的是字典中的key, 然后通过key 获取 value
for (NSString *key in dic) {
NSLog(@"%@", key);
//检索到key之后,通过objectForKey获取key对于的value
NSLog(@"%@", [dic objectForKey:key]);
}
//枚举器法
NSEnumerator *enumerator = [dic keyEnumerator];
id obj;
while (obj = [enumerator nextObject]) {
NSLog(@"%@", obj);
}
//对于可变字典来说,比不可变字典多了增加,删除, 修改操作
1.创建对象
NSMutableDictionary *mutableDic = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"Summer", @"name1", @"Sunshine", @"name2", @"Angle", @"name3", nil];
NSLog(@"%@", mutableDic);
2.求字典元素个数
NSInteger count = [mutableDic count];
NSLog(@"count = %ld", count);
3.删除元素
[mutableDic removeObjectForKey:@"name3"];
NSLog(@"%@", mutableDic);
[mutableDic removeAllObjects];
NSLog(@"%@", mutableDic);
批量删除
[mutableDic removeObjectsForKeys:[NSArray arrayWithObjects:@"name1", @"name2", nil]];
NSLog(@"%@", mutableDic);
4.添加元素
[mutableDic setObject:@"Kimi" forKey:@"name1"];
NSLog(@"%@", mutableDic);
5.修改key对应的value
[mutableDic setObject:@"Angle" forKey:@"name1"];
NSLog(@"%@", mutableDic);
6.setObject: forKey: 工作原理
先根据指定得key查找字典中是否存在相同的key.(查找字典中是否存在指定的key).如果存在则修改key对应的value.如果不存在,在字典中添加一个元素,key - value 键值对
集合类
集合
1.集合与数学集合一样,存储的元素互不相同(互异性)(唯一性).
2.存储的元素无序性
3.存储元素必须是对象类型
1.初始化方法
NSSet *set2 = [[NSSet alloc] initWithObjects:@"Summer", @"Sunshine", @"Angle", @"procedure", @"oriented", @"programming", @"Object", @"oriented", @"programming", nil];
NSLog(@"%@", set2);
2.获取元素个数
NSInteger count = [set2 count];
NSLog(@"count = %ld", count);
3.获取集合中的某个元素(为了方便性)
NSString *str1 = [set2 anyObject];
NSLog(@"%@", str1);
4.判断集合中是否包含某个对象
BOOL isExist = [set2 containsObject:@"Summer"];
NSLog(@"%d", isExist);
//可变集合
NSMutableSet *set2 = [[NSMutableSet alloc] initWithObjects:@"Summer", @"Sunshine", @"Angle", @"procedure", @"oriented", @"programming", @"Object", @1, @"programming", @"programming", @"programming", @1, @1,nil];
NSLog(@"%@", set2);
1.添加元素
[set2 addObject:@"Cindy"];
NSLog(@"%@", set2);
2.移除元素
[set2 removeObject:@"Summer"];
NSLog(@"%@", set2);
3.求带有记录对象重复次数的子类NSCountSet
NSCountedSet *countedSet = [NSCountedSetsetWithObjects:@"programming",@"programming",@"programming",nil];
//通过实例方法countForObject:来得到指定对象的重复次数
NSInteger count = [countedSet countForObject:@"programming"];
NSLog(@"count = %ld", count);
OC (五)
Block语法 Block使用 block方式实现数组排序
Block 匿名函数:没有名称的函数
block 类型: int (^)(int, int)
block 变量:myBlock
Block值: ^(int x, int y){ return 值};
eg:
//当把函数的实现赋给block变量之后,block变量就可以当做函数名使用
//当在block内部使用block外部定义的(局部变量)时,如果变量没有被__block修饰,则在block内部是readonly (只读的)不能对他进行修改,如果想修改,变量前必须要有__block修饰.
//__block的作用告诉编译器,编译时在block内部不要把外部变量当做常量使用,还是要当做变量使用
//如果在block中访问全局变量,就不需要被__block修饰
//求两个数的最小公倍数
__block int temp =0;
int (^myBlock)(int x,int y) = ^(int x, int y){
for (int i = x * y; i >0; i--) {
if ((i % x == 0) && (i % y == 0)) {
temp = i;
}
}
return temp;
};
int minCommonMultiple = myBlock(8,3);
printf("minCommonMultiple = %d\n",minCommonMultiple);
即:返回值类型(^block变量名)(参数类型) = ^(参数类型){ 函数体}; 其中 返回值类型可以省略。
Block 可以写在main函数内部。
为block的原有类型做新类型定义
typedef int(^newBlockType)(int, int);
//求两个数的最小公倍数
typedef int(^newBlock)(int,int);
__block int temp = 0;
newBlock myBlock = ^(int x, int y){
for (int i = x * y; i > 0; i--) {
if ((i % x == 0) && (i % y == 0)) {
temp = i;
}
}
return temp;
};
int minCommonMultiple = myBlock(8, 3);
printf("minCommonMultiple = %d\n", minCommonMultiple);
Block排序
1.对数组进行降序排序
//block 的排序方法 使用场景:适用于当数组中的对象类型是系统类型时,并且要降序排序时.
NSArray *sortArrDescending = [arr sortedArrayUsingComparator:^NSComparisonResult(id obj1,id obj2) {
//对block匿名函数实现,比较方法
//1.将id类型转为NSString *;
NSString *str1 = (NSString *)obj1;
NSString *str2 = (NSString *)obj2;
//2.字符串比较
return -[str1 compare:str2];
}];
NSLog(@"%@", sortArrDescending);
2.c语言的冒泡排序函数。对OC的数组对象做排序
//重新定义typedef
typedef BOOL (^CompareBlock)(NSString *,NSString *);
//声明方法
void bubbleSortArray(NSMutableArray *array,CompareBlock compare);
//实现方法
void bubbleSortArray(NSMutableArray *array,CompareBlock compare)
{
NSInteger count = [array count];//获取数组的元素个数
for (NSInteger i =0; i < count -1; i ++)
{
for (NSInteger j =0; j < count - 1 -i; j++) {
id object1 = [array objectAtIndex:j];
id object2 = [array objectAtIndex:j+1];
if (compare(object1,object2)) {
[array exchangeObjectAtIndex:jwithObjectAtIndex:j+1];
}
}
}
}
//对于可变数组进行排序NSMutableArray *array = [NSMutableArrayarrayWithObjects:@"21",@"1",@"23",@"1",nil];
__block int index =0;
CompareBlock compare = ^(NSString *str1,NSString *str2){
index ++;//比较的次数
int number1 = [str1 intValue];
int number2 = [str2 intValue];
if (number1 > number2) {
return YES;
}
return NO;
};
bubbleSortArray(array ,compare);
NSLog(@"array = %@",array);
NSLog(@"count = %d",index );
//对于不可变数组进行排序NSArray *array1 = [NSArrayarrayWithObjects:@"12",@"122",@"77",@"14",@"9",nil];
NSComparator compareBlock = ^(id str1,id str2){
int number1 = [str1 intValue];
int number2 = [str2 intValue];
if (number1 < number2) {
return NSOrderedAscending;//升序
}
return NSOrderedDescending;//降序
};
NSArray *sortArray = [array1sortedArrayUsingComparator:compareBlock];
NSLog(@"%@",sortArray);
OC (六)
1.创建日期对象
//创建的NSDate对象,获得的永远是0时区的时间.东八区,加八个小时.
NSDate *date1 = [NSDate date];
NSLog(@"%@", date1);
2.创建明天此时的日期
//时间间隔是以秒为单位
NSDate *tomorrowDate = [NSDatedateWithTimeIntervalSinceNow:24 *60 *60];
NSLog(@"%@", tomorrowDate);
//创建昨天此时的日期
NSDate *yesterdayDate = [NSDatedateWithTimeIntervalSinceNow:- 24 *60 * 60];
NSLog(@"%@", yesterdayDate);
//获取两个日期的时间间隔(tomorrowDate, yesterdayDate)
NSTimeInterval timeInterval1 = [tomorrowDate timeIntervalSinceDate:yesterdayDate];
NSLog(@"%f", timeInterval1 / 60 / 60 / 24);
NSTimeInterval timeInterval2 = [yesterdayDate timeIntervalSinceDate:tomorrowDate];
NSLog(@"%f", timeInterval2 / 60 / 60 / 24);
//比较日期的早晚
//(1)获得两个日期中较早的日期
NSDate *earlierDate = [tomorrowDate earlierDate:yesterdayDate];
NSLog(@"%@", earlierDate);
//(2)获得两个日期中较晚的日期
NSDate *laterDate = [tomorrowDate laterDate:yesterdayDate];
NSLog(@"%@", laterDate);
//(3)两个日期比较
NSComparisonResult result = [tomorrowDatecompare:yesterdayDate];
NSLog(@"%ld", result);(1, 0, -1)
//(4)获取当前日期的时间戳
NSDate *date = [NSDatedate];
NSTimeInterval time1970 = [datetimeIntervalSince1970 ];
NSLog(@"time1970 = %f", time1970);
//获取第二天得时间
NSDate *date1 = [NSDatedateWithTimeIntervalSince1970:time1970 + 24 * 60 * 60];
NSLog(@"date1 = %@", date1);
//将日期格式转换为字符串
//NSDateFormatter 是一个日期格式类, 将日期以一定的格式进行转换,(原理,转换成字符串). 另外也可将日期格式串转为NSDate对象
NSDate *date1 = [NSDate date];
//创建日期格式类对象
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
//指定日期格式
// hh 表示12小时制 HH 表示24小时制
// mm 表示分钟 MM 表示月份
// DD 表示当前日期在本年中的第几天
// ss 表示秒数
// 如果年份为两个y 只显示年份的后两位, 如果给其他个数的y.都是显示完整的年份
// 例如:MM 如果给两位.则月份如果是一位时, 前面补0;
[formatter setDateFormat:@"YYYY-MM-dd HH:mm:ss"];
//将日期按照指定的格式转换为日期格式串
NSString *dateStr = [formatter stringFromDate:date1];
NSLog(@"%@", dateStr);
//将日期格式串转换为NSDate对象
//@"2008-08-08 20:08:08"
NSString *dateStr = @"2008-08-08 20:08:08";
//创建日期格式化对象
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
//设置日期格式(一定要和日期格式串中日期的格式保持一致)
[formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
//将格式字符串转化为NSDate对象
NSDate *date = [formatter dateFromString:dateStr];
NSLog(@"%@", date);
//设置日期的格式
[formatter setDateStyle:NSDateFormatterFullStyle];
[formatter setDateStyle:NSDateFormatterLongStyle];
[formatter setDateStyle:NSDateFormatterMediumStyle];
[formatter setDateStyle:NSDateFormatterNoStyle];
[formatter setDateStyle:NSDateFormatterShortStyle];
//设置时间的格式
[formatter setTimeStyle:NSDateFormatterFullStyle];
[formatter setTimeStyle:NSDateFormatterLongStyle];
[formatter setTimeStyle:NSDateFormatterMediumStyle];
[formatter setTimeStyle:NSDateFormatterNoStyle];
[formatter setTimeStyle:NSDateFormatterShortStyle]
类目
Category 也叫分类 或类目
主要作用是为没有源代码的类添加方法,
通过Category添加的方法会成为原类的一部分。从而达到扩展一个类的功能
category | entension | |
作用 | 为没有源代码的类添加方法 | 管理类的私有方法 |
格式 | 定义一对.h 和.m | 把代码写到原始类的.m文件中 |
Category | Subclass | |
功能 | 只能为类添加方法 | 既能为类添加方法还可以添加变量 |
特点 | 新添加的方法会成为原始类的一部分,能被子类继承 | 新添加的方法只有子类才有,父类不具有 |
使用 | 使用原始类的实例(如果是-方法)或者原始类(如果是+方法)调用方法 | 子类才能调用,如果在项目开发到尾声的时候使用子类添加了方法,需要对已写代码做类型替换(将父类替换为子类)Category和Extension的区别 |
Protocal
协议是一套标准(一堆方法的声明)只有h文件。
接受协议的对象实现协议中定义的方法。
协议就像一张任务清单(或便利贴),上面写了一对才需要处理的事,清单交给谁,谁就要去完成清单上规定的任务。
协议定义好之后,需要有类去遵守这个协议,实现协议里的方法
<>表示服从协议,服从的协议写在<>之内
遵守协议即在.h文件的父类名后写上<协议名>。实现协议的方法即在.m文件中实现协议中的方法。相当于给这和类添加了若干个方法。这个类的实例就可以调用这些方法。
类的扩展的四种:继承 category(针对系统类) extension (针对自定义类做扩展)protocol(针对可以看到.m文件的类)。
Protocol的核心使用场景是实现Delegate设计模式。
关键词:
@optional可选的,被他修饰的方法服从协议的类可以选择实现(可以实现,也可以不实现)
@required 必须的被他修饰的方法服从协议的类必须实现(没有商量的余地).
Person *summer1 = [[Personalloc] init];
//调用必须完成的协议,直接调用 (@required)
[summer1 teachMemeory];
//如果调用非必须完成的协议需要一般都先判断teachKVC方法是否在Person类中定义.如果定义了才可以调用.
if ([summer1respondsToSelector:@selector(teachKVC)]) {
[summer1 teachKVC];
}
总结:
NSDate 是iOS中表示日期的类。可以使用NSDateFormatter 控制 NSString 和NSDate 的互转。
Category能为没有源代码的类添加方法。
Extension为类管理私有方法 Protocol和代理通常联合使用,是ios中特别重要的语法
OC (七)
setter 方法的作用:为单一的变量赋值.
setter 方法规范写法:1.- 号方法,无返回值. 名字以set开头 + 要设置的变量的实例变量名(首字母要大写):(setter方法有且只有一个参数) + 参数的类型(和实例变量类型相同) + 参数名(和实例变量名相同)
getter 方法的作用:获取单一的实例变量的值.
getter 方法的命名规范: - 号方法,有返回值 (返回值类型和实例变量类型相同) 方法名直接和实例变量名相同. 无参数
属性的声明:使用@property 声明属性例如:(@property NSString *name;)
- 相当于 @interface 中声明了两个方法:
- - (void)setName :(NSString *)name;
- - (NSString *)name;属性的实现:使用@synthesize 实现属性(例如:@synthesize name = _name)
- 相当于@implementation实现了
- - (void)setName:(NSString *)name;
- - (NSString *)name;
@synthesize name = _name;
- 注释:
- @synthesize 属性的实现部分,实现属性自动生成的setter 以及 getter方法.
- name 属性名,指定要实现哪一个属性生成的setter 以及 getter 方法
- _name,指定setter 和 getter方法内部所要操作的实例变量
如果 指定的实例变量没有定义,系统会自动生成指定的实例变量 ,但是生成的实例变量是私有的,子类不能直接访问
如果实例变量想让子类访问, 那在.h文件必须定义实例变量.
如果未指定setter 和 getter 内部所要访问的实例变量时, 系统自动生成一个和属性名一样的实例变量 例如:@synthesize name, gender, age, height, weight;
如果对于setter和getter方法,如果我们实现了,系统就不会再自动生成了
如果在.m文件中未通过@synthesize对属性进行合成,系统会自动合成,只不过此时系统默认的setter和 getter方法内部所操作的实例变量是 _属性名
如果将@synthesize省略,并且我们自己实现setter以及 getter方法时,系统就不会生成对应setter以及 getter,还有实例变量
如果@synthesize不省略,并且我们自己实现setter以及 getter方法时,系统还会生成实例变量,如果不自己实现setter 和 getter方法,系统照样给实现
属性的特性(attribute)
第一大特性:读写特性
(1)readonly:告诉编译器,属性在自动生成方法时,只会生成getter方法,不会生成setter方法.
(2)readwrite:告诉编译器,属性在自动生成方法时,既要生成setter方法,也要生成getter方法.系统默认的读写特性
(3)setter = aa:告诉编译器,当自动生成setter方法时,setter方法的方法名为指定的名字aa,不采用默认的.
(4)getter = bb:告诉编译器,当自动生成getter方法时,getter方法的方法名为指定的名字bb,不采用默认的.
第二大特性:原子性特性
(1)atomic:原子特性,保证线程安全,内部做了安全性访问理(加锁与解锁).系统默认的原子特性
(2)nonatomic:非原子性,不保证线程安全
因为对于setter以及 getter方法的使用,比较频繁,在一段时间内可能要多次访问,所以使用atomic会非常消耗系统的资源,降低程序的执行效率,使用nonatomic虽然不保证线程的 安全,但是使用一般情况下都是安全的,所以对于原子特性,使用nonatomic.
第三大特性:语义特性
(1)assign:直接赋值,使用针对于基本数据类型(int float).也可针对于对象类型.系统默认的语义特性.
(2)copy:针对于对象类型并且要服从NSCopying协议的对象才可以.会复制出一个新的对象,拥有新的对象的所有权.(引用计数 + 1).(先暂时这么理解)
(3)retain:针对于对象类型,会造成对象的引用计数+ 1
setter 以及 getter方法,内部是对实例变量赋值以及实例变量的取值操作,所以方法内部要操作实例变量对内部实现的解释 if (_name != name) //判断原有对象和新对象是否是同一个,如果是同一个就没有必要重新赋值了.否则 会先release, release之后空间被系统收回,此时再retain就成为野指针问题了.
[_name release];//释放保有的之前对象的所有权
_name = [name retain];//让实例变量_tea 保有新的对象的所有权 [tea retain]就是将tea的引用计数加一,返回该空间,_tea = [tea release] 就是将返回的空间赋给_tea,两个空间的指向是同一个空间
- 1.当把语义特性声明为assign时, setter 以及 getter 方法内部实现
- - (void)setName:(NSString *)name
- {
- _name = name;
- }
- - (NSString *)name
- {
- return _name;
- }
- 2.当把语义特性声明为retain时, setter 以及 getter 方法内部实现
- - (void)setName:(NSString *)name
- {
- if (_name != name) {
- [_name release];
- _name = [name retain];
- }
- }
- - (NSString *)name
- {
- return [[_name retain] autorelease];
- }
- 3.当把语义特性声明为copy时, setter 以及 getter 方法内部实现
- - (void)setName:(NSString *)name
- {
- if (_name != name) {
- [_name release];
- _name = [name copy];
- }
- }
- - (NSString *)name
- {
- return [[_name retain] autorelease];
- }
点语法 ,是一种快速访问 setter以及 getter方法的方式.声明属性默认生成对应的setter以及 getter方法.所以属性和setter和 getter方法都有关联
对象.属性名 ---如果在等号的左边,意味着调用setter方法,除此情况,都是调用getter方法
OC (八)
内存溢出丶野指针异常
系统在一直分配空间的时候会出现内存溢出。
对象内存空间已经被系统回收,仍然使用指针操作这块内存。野指针异常是程序crash的主要原因。代码量越大的程序,越难找出出现野指针的位置。
内存管理的方式
垃圾回收(gc)
MRC (Manual Reference Count)、
手动引用计数:内存的开辟和释放都由程序代码进行控制。相对垃圾回收来说,对内存的控制更加灵活,可以在自己需要释放的时候及时释放,对程序员的要求较高,程序员要熟悉内存管理的机制。
ARC(Auto Reference Count)
ios支持的两种机制: MRCARC
MRC的内存管理机制是:引用计数。
OC采用引用计数机制管理内存,当一个新的引用指向对象时,引用计数器就递增,当去掉一个引用时,引用计数就递减。当引用计数到零时,该对象就将释放占有的资源。
影响计数的方法:
+alloc :开辟空间,让被开辟的空间的引用计数变为1.这是由0到1的过程。
-retain :开辟内存空间 如果内存空间之前引用计数为1,retain之后变为2 如果引用计数是5,retain之后变为6
-copy:把某一内存区域的内容拷贝一份,拷贝到新的内存空间里去,被拷贝的区域引用计数不变,新的内存区域的引用计数为1.
- release :引用计数减一,如果内存空间之前引用计数为4,release 之后变为3,如果之前引用计数为1,release之后变为0,内存被系统回收。
-dealloc
dealloc是继承自父类的方法,当对象引用计数为0的时候由对象自动调用。
-autorelease :未来的某一时刻引用技术减一,如果内存之前引用计数为4,autorelease之后仍然为4,未来某个时刻会变为3.
内存管理原则
引用计数的增加和减少相等,当引用计数降为0之后,不应该再使用这块内存空间。
凡事用allocretain 或者copy让内存的引用计数增加了。就需要使用release或者autorelease让内存的引用 计数减少。在一段代码内。增加和减少的次数要相等。
autoreleasepool的使用
通过autoreleasepool控制autorelease对象的释放
向一个对象发送autorelease消息。这个对象何时释放取决于autoreleasepool
copy方法
跟retain不同,一个对象想要copy,生成自己的副本,需要实现NSCopying协议,定义copy的细节(如何copy)如果类没有接受NSCoping协议而给类发送copy消息,会引起crash
总结:
OC借助引用计数机制去管理内存,凡是使用了alloc copy retain 等 方法,增加了引用计数,就要使用release 和autorelease 减少引用计数,引用计数为0的时候,对象所占的内存,被系统回收。
autorelease是未来某个时间(出autorelease)引用减一,不是即时的。
不是任何对象都可以接受copy消息。只有接受了NSCoping协议的对象才接受copy消息。
//面试题一
@autoreleasepool {
for (long i = 0; i < 100000000; i++) {
@autoreleasepool {
Person *per = [[Person alloc] init];
[per autorelease];
}
}
}
//面试题二
//常量区内存系统管理
//内存泄漏
//涉及三个区(栈区堆区 常量区)
@autoreleasepool {
NSString *per = [[NSString alloc] init]; //0 - 1
[per retain]; //1 - 2
[per retain]; //2 - 3
per = @"aa";
[per release]; //3 - 2
[per rele7;ase]; //2 - 1
[per release]; //1 - 0
}
1.dealloc
什么是dealloc
dealloc是NSObject的实例方法,与alloc对应于回收开辟的内存空间
这个方法在对象引用计数为0的时候,由系统自动调用,
通常我们在dealloc中释放类的实例变量的所有权。
注意:
永远不要手动调用dealloc
在dealloc方法的最后一行,必须要写[super dealloc];
2.便利构造器的内存管理
return [p autorelease]是最 完美的解决方案,既不会内存泄露,也不会产生野指针。
3.collection的内存管理
collection就是NSArray NSDictionary NSSet。。。等容器类
collection会自主管理自己内部的元素
collection内存的自主管理
加入collcetion中的对象会被retain
移除出collection的对象会被release
collection被释放会对内部所有对象release
4. 多态的特点
父类指针可以指向不同的子类对象
允许在多个类中定义同一个消息接口
可以屏蔽不同子类对象之间的差异,写出通用代码。
适应需求的不断变化。<NSCopying>协议的实现
- (id)copyWithZone:(NSZone *)zone
{
Teacher *newTea = [[TeacherallocWithZone:zone] init];
newTea.name =self.name;
newTea.gender =self.gender;
return newTea;
}
//便利构造器内部实现
+ (id)teacherWithName:(NSString *)name gender:(NSString *)gender
{
Teacher *teacher = [[Teacheralloc] initWithName:name gender:gender];
return [teacher autorelease];
}
//便利构造器内存管理
Teacher *teacher = [Teacher teacherWithName:@"Angle"gender:@"female"];
NSLog(@"%lu", [teacher retainCount]);
NSLog(@"TEST");
//便利构造器,内存堆积
for (long long i = 0; i < 10000000000; i++) {
NSString *str = [NSString stringWithFormat:@"aaaa"];
}
//collection 的内存管理
Teacher *tea1 = [[Teacher alloc] initWithName:@"Summer" gender:@"female"]; //0 - 1
Teacher *tea2 = [[Teacher alloc] initWithName:@"Cindy" gender:@"female"]; // 0 - 1
NSMutableArray *arr = [[NSMutableArray alloc]initWithObjects:tea1, tea2,nil];
//1.当把一个对象放入集合(数组.字典,集合)中时,会将对象的引用计数 +1, 因为内部做了retain操作
//2.当collection(字典数组集合),空间被回收时,他们会向容器中的每一个元素都发送一个release消息(对应添加元素时的retain操作)
NSLog(@"%lu", [tea1 retainCount]);
NSLog(@"%lu", [tea2 retainCount]);
[tea1 release];
[tea2 release];
//3.当从collection(数组,字典,集合)中移除一个元素时,会release该对象,引用计数 -1.
[arr removeObjectAtIndex:1];
[arr release];
//内存直接赋值,引用计数不会变化(assign)
总结:
assignretina copy 对应不同的setter实现。为实例变量赋值时,尽量使用setter方法。再次赋值时,会把之前的值release。
dealloc 在对象引用计数为0时自动调用,不要显式调用。dealloc实现体内,先释放实例变量,然后执行[super dealloc];
便利构造器的内存管理是借助autorelease实现的。
集合会管理自己的元素。
OC (十)
KVC
KVC : key-value-coding 键值编码,是一种间接访问实例变量的方式,通过指定的key来找到对应的实例变量.(切记:key是字符串)
KVC 的工作原理:根据指定的key,比如:name,
(1)先查找该类是否有对应的setter方法:比如:setName:如果有,则通过setter方法给实例变量赋值,如果没有,则转到(2)
(2)查找该类有没有一个_ + key的实例变量,比如_name,如果有,则给_name赋值,如果没有,则转到(3)
(3)查找该类是否具有一个和key同名的实例变量,比如name.如果有,则给name赋值,如果没有,则转到(4)
(4)如果都没有找到,系统会抛出一个NSUnknownKeyException,说明没有找到可匹配的key(也就是该类既没有setName:,也没有_name,也没有name).
错误解决方案:
//setValue : forKey:
//当给定的key未匹配到对应的方法以及实例变量时,会自动调用该方法,所以我们只需要实现该方法即可,(实现体中可以没有实现内容)
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
// NSLog(@"undefinedKey");
}
//valueForKey
//当给定的key未匹配到对应的方法以及实例变量时,会自动调用该方法,
- (id)valueForUndefinedKey:(NSString *)key
{
return nil;
}
//防止没有的实例变量出现crash
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
}