对象作为方法的参数
- (BOOL)isOlderWithOthetDog:(HMDog *)otherDog
{
if(_age > otherDog->_age)
return YES;
return NO;
}
#define LOGBOOL(val) NSLog(@"%@",val == YES ? @"YES":@"NO")
LOGBOOL(res);
异常处理
- 错误
- 一般情况下指的是我们的程序员代码有语法错误.
- 错误的后果就是无法编译.更谈不上执行.
- 解决方案:将错误的代码修改为符合语法规范的代码就OK了.
- BUG
- 一般情况下指的是我们的程序员代码有语法错误.
计算机还是很庞大的体积的时候,一只虫子使电路板断路造成停摆,找到以后就讲计算机的错误命名为BUG了
- 指的是,程序可以编译,链接,执行.只不过程序执行出来的记过并不是我们所预想的那样.
- 解决方案: 99%的Bug都是因为变量的值没有按照我们所预想的那样去变化.通过断点调试去监视变量的值是如何变化的,分析为什么会乱变化.
- 异常
- 概念:指的是程序可以编译,链接,执行.当程序在运行的时候,处在某种特殊情况下的时候,程序的执行就会报错.
- 特点:不是一定会发生,而是在处于某种特定情况下的时候才会发生.
- 后果:程序立即终止运行,后面的代码不会执行了.
- 异常的处理
- 处理的目的,希望程序发生异常不要崩溃,而是继续往下执行.
- 使用@try…@catch来处理异常
- 将有可能发生异常的代码使用@try围起来
@try
{
在执行的时候有可能会发生异常的代码;
}
@catch(NSException *ex)
{
}
- 执行步骤
a. 如果@try中的代码在执行的时候,发生了异常,这个时候程序不会崩溃,而是会立即跳转到@catch中执行代码.@catch中的代码执行完毕后,再继续往下执行
b. 如果@try中的代码没有发生异常,就跳过@catch,继续往下执行,- 作用:让我们程序在崩溃的时候,继续往下运行.
- 使用注意,如果发生了异常,会将发生异常的信息,封装为一个NSException对象.再把对象的地址赋给*ex,再执行@catch.
- 在@catch中如果想拿到发生异常的原因,就可以访问参数对向 方法是
NSString *re = [ex reason];
@catch中一般写处理异常的代码
后面还可以跟一个
@finally
{
无论有没有发生异常,都执行finally中的代码
}
- try并不是万能的,不是所有的异常都可以处理. C语言的异常处理不了,OC的也不是全部都能处理.该崩还是崩.所以平时写代码很少用.
我们一般通过逻辑判断来处理异常
if(num2 == 0)
{
NSLog(@”除数不能为0”);
}
else
{
int num3 = num1/num2;
}
类方法的基本使用
类方法不用依赖于对象,通过类名就可以调用
- 声明
类方法使用+号声明,跟对象方法相反
+ (void)classMethod
{
代码
}
调用不需要创建对象,直接使用类名
[HMperson classMethod];
==类方法只能通过类名调用,不能通过对象名==
- 类方法的特点
- 节约空间,不需要创建对象.对象方法还要先创建一个对象.
- 提高效率.对象方法通过对象找到堆中的isa,再找到类再调用方法. 类方法直接调用.
- 局限性
- 无法直接访问类的属性
- 不能通过self去调当前类的对象方法
- 执行类方法的时候可能还没有对象,那也就访问不了属性.也访问不了对象的方法.
- 如果方法不需要访问属性,也不需要直接调用当前类的方法,就可以定义为类方法节约空间.
注意的几个问题
- 对象方法和对象方法不能重名,类方法之间也不行,但是类方法和对象方法可以同名
- 通过类名就是调用类方法,通过对象名就是调用对象方法
- 对象方法只能通过对象调用,类方法只能通过类调用.
- 类方法可以创建一个对象,访问这个对象的对象属性和对象方法.
- 对象方法中可以通过类名来调用类方法.
类方法的规范
- 我们写一个类,就要提供一个和类名同名的类方法.返回一个纯洁的对象.对象的属性的值都是默认值.
+ (HMPerson *)person
{
HMPerson *p1 = [HMPerson new];
return p1;
}
- 为什么这么做,因为这是规范,苹果也这么做.
NSArray *arr = [NSArray array];
使用同名的方法.而不是new.
- 如果希望创建对象的值是创建者指定的,那就可以为这个类方法带参数
+ (HMPerson *)person;
+ (HMPerson *)personWithName:(NSString *)name andAge:(int)age
{
HMPerson p1 = [HMPerson new];
p1->_name = name;
p1->_age = age;
return p1;
}
//这个时候创建的时候就可以一起赋值,后面经常会用到.写起来更简单
HMPerson *p2 = [HMPerson personWithName:@"jack" andAge:18];
在方法内部创建对象,并初始化对象的属性,并返回.
- 当你创建一个NSAaray对象的时候,有一个aaray方法,N个aarayWith方法.
- new 其实是一个类方法.定义在NSObject里面的.创建一个对象.
NSString类
- NSString是一个数据类型,保存OC字符串
- 实际上是一个类
既然是类拿完成创建的方式应该是
NSString *str0 = [NSString new];
NSString *str1 = [NSString string];
//这个是一个空字符串,里面什么都没有 @"" , 不是nil
//再把字符串存储到这个对象中
- 因为NSString是OC中最常用的一个对象
- 如果每次都这么创建字符串对象的话,就太累了
- 以苹果为我们提供了一个快捷方法 @”“
- 这是一个创建NSString对象的简写方式
- @”jack” 是一个NSString对象,创建了以后在把字符串的地址保存到str.
- %@ 代表打印指针指向的对象.用%p打印就是打印@”jack”这个对象的地址.
- 既然@”jack”是一个类,那肯定就有方法.str里面应该可以访问.常用的类方法
- 类方法stringWithUTF8
将C语言的字符串转换为OC字符串对象.
+ (nullable instancetype)stringWithUUTF8String:(const char*)nullTerminatedCString;
char *name = "杰克;
NSString *str = [NSString stringWithUTF8String:name];
NSLog(@"str = %@",str);
想让用户从控制台输入进来,接到的都是C字符串,这个可以把接收的字符串转换为OC保存起来.
char name[20];
fgets(name,20,stdin);
size_t len = sstrlen(那么);
if(name[len-1] == '\n')
{
name[len-1] = '\0';
}
p1->name = [NSString stringWithUTF8String:name];
- stringWithFormat
将变量拼接乘一个新的OC字符串*******
int age = 19;
NSString *name = @"jack";
要拼接一个新的字符串,不是打印
NSString *str = [NSString stringWithFormat:@"大家好,我叫%@,我今年%d岁",name,age];
- 常用的对象方法 计算OC字符串长度
NSUInteger len = [str length];
//是 NSUInteger 其实就是 unsigned long 类型
NSLog(@"%lu",len);
==OC中中文是1个长度!!==
- 得到指定下标的字符
NSString *str = @"abc打瞌睡的小男孩!";
unichar ch = [str characterAtIndex:3];
//unichar 是 unsigned short 2个字节.根据情况占的字节不一样,英文一个中文两个.
NSLog(@"ch = %c",ch);
//%c只打印一个字节,所以要用大写的C
NSLog(@"ch = %C",ch);
//打印unichar字符用大C,会自动探测,不会打错.
==OC中一个中文字符占两个字节!!==
- 判断两个字符串的内容是否相同**
NSString *str1 = @"jack";
NSString *str2 = @"rose";
//strcmp只能判断C语言的
//str1 == str2 也可以判断,但不要使用,会出问题.
- (NSComparisonResult)compare:(NSString *)string;
NSComparisonResult res = [str1 compare:str2];
1. 返回的是一个枚举值,NSOedereAscending 表示当前字符串str1比str2小.
2. NSOrderedSame 代表一样
3. NSOrdereDescending 当前str1比str2大.
4. 比较的是ASCII码
5. 枚举值烦可以用int接 小是-1,一样是0,大了是1.
- 只判断是否相等
BOOL res = [str1 isEqualToString:str2];
不一样是0,一样是1;
匿名对象
- 如果函数有返回值,我们可以不使用变量接收返回值,而是直接将函数写在要使用其返回值的地方.
- 正常情况下,我们创建对象,是使用1个指针保存了对象的地址.new方法创建,初始化对象,返回地址.
- 匿名对象就是没有名字的对象,不用指针去存储对象的地址.
- 使用
[HMPerson new]->_name = @"jack";
HMPerson *p = [HMPerson new];
[p goHomeWith:[HMCar new]];
//直接给回家方法里面的参数一个匿名对象.
- 因为匿名对象没有名字,所以只能创建出来的时候用一次
- 如果一个对象成员你只需要赢一次没就可以使用匿名变量
- 如果后面还想用就不能用匿名对象了.
面向对象的三特性:封装
封装:将很多小东西塞在一个大口袋里面.
- 对外部屏蔽了内部的实现.
- 方便管理
- 函数或方法就是一种封装的体现.
- 类是一种更高级的封装,将1个类的状态和行为封装在了一个类中,我们只需要直到这个类是干嘛的.
遇到的问题 赋值
- 对象的属性赋值的时候,语法上只要赋一个和属性的类型相同的数据都可以
- 但是情理上比如年龄int类型不能为负数.应该是0-200的整数,超过这个数据有悖于常理.
- 用数据类型又不精确不靠谱,枚举也枚举不完
- 解决方案:
- 先把类的@public属性去掉,外部就不能访问赋值.不能赋值
- 专门设置一个方法为属性赋值,setter
1. 这个方法一定是个对象方法,因为要为属性赋值,所以也没有返回值
2. 方法的名字一定是set开头.再跟上属性名去掉下划线,首字母大写.
3. 方法一定有参数,类型跟属性类型一致.
- (void)setAge:(int)age
{
if(age >= 0 && age <= 200)
_age = age;
//如果不符合要求就做默认处理.
_age = 18;
}
调用
[p1 setAge:100];
4. 这样就只能使用setter方法来赋值
5. 如果符合逻辑就赋值,否则就不会.
- 取值getter
这样一来我们的属性也不能通过箭头取出了,所以要再写一个方法取值
1. 要返回属性的值,一定是一个对象方法,所以也一定有返回值
2. 返回值类型和属性类型一致
3. 名字就是属性的名字去掉下划线.
4. 这个方法一定是没有参数的
- (int)age
{
return _age;
}
int age = [p1 age];
5. 想得到对象的属性值就必须要用对象的getter方法.
只读与只写封装.
- 一个属性比如年龄只允许外界取值,就只写getter 不写getter.年龄是自动增长的.就是==只读封装==
- 属性的封装只有setter,只能调用setter方法赋值不能取值.就叫做==只写封装==
属性封装的规范
只要属性需要被外界访问,无论取值赋值有没有逻辑验证,都要为其封装setter或getter.必须封装,不然点语法用不了.==码农==����
- (void)setDog:(HMDog *)dog;
{
_dog = dog;
}
- (HMDog *)dog
{
return _dog;
}
类与类之间的关系 ==面试题==
- 组合关系 由多个对象组合起来的. 人是有 头 身体 脚 组成的
- 依赖关系 人没有手机,需要穿一个手机对象的参数才能打电话 有可能是借来的反正不是他的
- 关联关系 人的属性里面有一部手机,可以直接[_phone call] 自己就有一部手机
- 继承关系
士兵突击
使用面向对象模拟:士兵开枪,枪射出子弹
名字找类 士兵类 枪类 子弹类
枪属性: 型号,颜色,子弹数量
行为:射击
士兵属性:姓名 兵种 枪
行为:开枪
static关键字
- C语言的static 可以修饰局部变量,将局部变量变为静态变量 修饰全局变量,函数
- OC中static
- static 不能修饰属性和方法
- 方法中的局部变量可以修饰,在方法执行完后不会被回收.下次直接用,和c语言修饰局部变量一样
- 如果希望方法无论执行多少次,变量只有一个,那么就搞成静态的
- static 不能修饰属性和方法
self关键字
- 考虑的问题
- 在方法中是可以定义一个和属性名相同的局部变量,这个时候,如果直接使用同名的变量,访问的是局部变量
- 如果我们就是要访问属性怎么办呢?
- 就是要在1个对象方法中调用当前对象的另外一个对象方法呢?
self关键字
- 可以用在对象方法和类方法中
- self是一个指针.在对象方法中self指向当前对象.在类方法中self指向当前类.
- 在对象方法中 使用self
谁调用这个对象方法,谁就是这个对象方法的当前对象.
[p1 sayHi];
1. p1 就是当前对象
2. self的值是当前对象的地址
3. %p 的p1 和 self 是相等的
- 可以使用self关键字显示的访问当前对象的属性.
- 可以使用self关键字来显示的调用当前对象的方法.
- 必用场景
{
1. 有和属性同名的局部变量,要调用属性就必须用self->去调用属性.
2. 如果想在对象方法中调用当前对象的其他的对象方法必须是用self->.
3. 如果你重新创建对象,调用的就不是当前对象的方法而是新对象的.
}
4. 选用场景
5. 在方法中如果没有和属性同名的局部变量,这个时候要访问属性名和
6. 属性的名字才以下划线开头,局部变量不要以下划线开头.这样写是不会出现重名的.
- 在类方法中使用self
- 类加载
当类第一次被访问的时候,就将这个类储存在代码段.一旦存储,直到程序解释才会被释放
- 在类方法中self也是一个指针,这个指针指向当前这个类在代码段中的地址
- 如何拿到类在代码段中的地址呢?
1. 查看对象的isa指针
2. 在类方法中打印self的值
3. 调用对象的方法class. 也可以得到这个对象所属的类的地址
4. 调用类的类方法class 也可以得到这个类所在的地址
- 类加载
[p1 class];
//返回p1方法的类在代码段中的值.
[HMPerson class];
//类在代码段中的地址
- 我们现在知道:在类方法中, self确实指向当前这个类的.然而有什么用??
在类方法中,self代表当前这个类.self == HMPerson
在类方法中,可以使用当前类的地方,完全可以用self代替
在类方法中可以使用self来代替当前类. 比如调用当前类的其他类方法.
声明变量的时候不能用self 他不是类型. HMPerson *p1 的是类型 self是指针不能代替.
[HMPerson new]可以用self代替
继承
- 多个类具有相同的成员.
- 使用复制粘贴缺点:代码冗余.后期维护不好维护.
- 继承的目的就是让子类继承父类的成员,而不用自己定义
- 步骤
“`
@interface 类名 : 父类名
@end
@implementation 类名
@end
在声明类的时候.在类名的后面,冒号后面写上父类的名字
子类就拥有所有父类的成员和方法.