OC基础(三)—— 从方法到封装
<a>目录
<a>
类方法
NSString
匿名对象
属性的封装(setter、getter)
对象之间的关系
</a>
类方法
-
在1个Target中无法访问另外1个Target中的类.
-
OC中的方法分为两种.
1).对象方法/实例方法:
我们之前学习的方法 就叫做对象方法. 如果想要调用对象方法就必须要先创建对象 通过对象名来调用.
2).类方法.
类方法的调用不依赖于对象.如果要调用类方法 不需要去创建对象.而是直接通过类名来调用.
-
声明.
1).对象方法的声明:
对象方法声明使用
-
号- (返回值类型)方法名; - (void)sayHi;
2).类方法的声明:
类方法声明使用
+
号.-(返回值类型)方法名;
和对象方法的声明实现除了
+
和-
其他的都是一样的 -
调用.
1). 对象方法: 先创建对象 再通过对象名来调用.
2). 类方法: 不需要通过对象名来调用 所以如果要调用类方法不需要创建对象.而是直接使用类名来调用
[类名 类方法名];
-
分析一下 类方法和对象方法的调用过程.
类方法的特点
1). 节约空间: 因为调用类方法不需要创建对象. 这样就节约了空间.2). 提高效率: 因为调用类方法不需要拐弯 直接找到类 直接执行类中的类方法.
-
在类方法中不能直接访问属性.
1). 属性是在对象创建的时候.跟随着对象一起创建在对象之中.
2). 类第1次被访问的时候,会做类加载. 是把类的代码存储在代码段
因为属性只有在对象创建的时候才会创建在对象之中.
而类方法在执行的时候.有可能还没有对象. 对象都没有 你访问个毛的属性.
虽然不能直接访问属性. 但是我们可以在类方法中创建1个对象 访问这个对象的属性
-
在类方法中也不能通过self直接调用当前类的其他的对象方法
因为对象方法只能通过对象来调用 而这个时候没有对象.
-
在对象方法中可以直接调用类方法.
-
什么时候我们可以将方法定义为类方法呢?
如果方法不需要直接访问属性 也不需要直接调用其他的对象方法.
那么我们就可以将这个方法定义为类方法这样就 节约空间 提高效率.
-
关于类方法的规范.
1). 如果我们写1个类,那么就要求为这个类提供1个和类名同名的类方法.
这个方法创建1个最纯洁的对象返回.因为苹果和高手写的类都遵守这个规范.
2). 如果你希望创建的对象的属性的值由调用者指定 那么就为这个类方法带参数.
类名+WithXXX: ……
NSString
-
NSString是1个数据类型.用来保存OC字符串的.
-
其实NSString是Foundation框架中的1个类.
作用: 存储OC字符串的
所以.OC中的字符串本质上是用NSString对象来存储的. -
其实完整的标准的创建NSString对象的方式是:
NSString *str0 = [NSString new]; NSString *str1 = [NSString string]; 这种方式创建的字符串是空字符串 @"" 但是.NSString是我们最常用的1个对象 所以OC使用了1种更为简单的方式来创建字符串对象. 直接使用@来表示1个OC字符串. @"jack" 这个其实本质上是1个NSString对象. NSString *str = @"jack"; @"jack" 是1个NSString对象 str的值是这个对象的地址. %p 打印的是指针变量的值 %@ 打印的是指针变量指向的对象.
-
NSString最常用的两个类方法.(到目前为止第二个用到的最多)
1). + (instancetype)stringWithUTF8String:(const char *)nullTerminatedCString; instanceType 作为返回值 代表返回的是当前这个类的对象. 作用:将C语言的字符串转换为OC字符串对象. 2). + (instancetype)stringWithFormat:(NSString *)format, ... 使用频率100分. (这里我想加一点自己的理解:这个就用到了上面所说的,用类方法创建对象,创建对象的时候由调用者来指定对象的属性,那就传入了参数format) 作用: 拼接1个字符串对象.使用变量或者其他数据拼接成OC字符串. int age = 19; NSString *name = @"小明"; //@"大家好我叫xx 我的年龄是xx" NSString *str = [NSString stringWithFormat:@"大家好,我叫%@,我今年%d岁",name,age]; NSLog(@"str = %@",str);
-
最常用的对象方法
1). length方法 返回值为NSUInteger其实就是unsigned long. 得到字符串的字符的个数 可以处理中文.中文长度是:一个汉字代表1 2). 得到字符串中指定下标的字符. - (unichar)characterAtIndex:(NSUInteger)index; 返回值是unichar 其实就是unsinged short 占据2个字节. 如果要输出unichar变量的值使用%C 钛金眼睁大 这是大写的C 3). 判断两个字符串的内容是否相同. a. 判断两个字符串的内容是否相同 不要用==去判断 因为这样有可能会出问题. b. 判断相等的方式. - (BOOL)isEqualToString:(NSString *)aString; 所以,判断两个字符串是否相等 应该使用这个方法区判断 才会得到正确的结果. 4).比较字符串的大小. - (NSComparisonResult)compare:(NSString *)string; 完全可以使用int类接收结果. 因为返回值是1个枚举. 返回值如果是 -1 说明 小于 0 等于 1 大于
匿名对象
(我在push控制器的时候经常用匿名对象,前提是这个需要push的控制器不需要定制什么属性)
-
我们之前创建对象的做法.
Person *p1 = [Person new];
让1个指针指向1个对象 这个指针就叫做这个对象的名字. -
匿名对象.
没有名字的对象,如果我们创建1个对象,没有用1个指针存储这个对象的地址.
也就是没有任何指针指向这个对象 那么这个对象就叫做匿名对象。 -
如何去使用1个匿名对象呢?
因为new实际上1个类方法. 这个方法做的事情创建对象(4个步骤). 返回值是创建的对象的地址.
[Person new] 这句代码的结果实际上就是创建的那个对象的指针. 那我们可以直接使用.
[Person new]->_name = @"jack";
[[Person new] sayHi];
-
注意点.
1). 匿名对象只能使用1次.
2). 每次创建匿名对象都是不同的对象 。
[Person new]->_name = @"jack";
创建了1个对象[Person new]->_age = 18;
又创建了1个对象
[[Person new] sayHi];
第3个对象. -
有神马用?
1). 如果某个对象的成员只会被我们使用1次.用完之后这个对象再也不需要了 那么就可以使用匿名对象.
2). 如果方法的参数是1个对象,而调用者为这个参数赋值的对象 就是专门来给这个方法传递的
并且这个对象调用者不会使用 那么这个时候就可以直接为方法传递1个匿名对象 。(简单点说,就是调用者用这个对象只是为了来调用某个方法的,并不会再使用它)
属性的封装
-
面向对象的三大特征是什么?
封装
类就是更高级别的封装.类将数据和行为封装为了1个整体.
好处:
-> 屏蔽内部的实现.外界不需要知道内部是如何实现的 只需要知道这个对象有什么用.
-> 方便操作.
-> 后期的维护十分的便利.继承
多态
-
封装功能的时候可以引出一个问题.
-
就是为对象的属性赋值的时候 语法上其实只要 数据的类型 是 属性的类型就是可以的.
但是,情理上这么做是不合的.
比如Person对象有1个int类型的属性_age 表示1个人的年龄.
为这个对象的_age属性赋值的时候 其实只要是int类型的数据 语法上都是完全可以的.
但是1个人的年龄 正常情况下是在 0-200之间的. -
如何解决:
在为对象的属性赋值的时候,我希望将这个数据做1个逻辑验证.
如果为属性赋的值在这个逻辑范围之内 那么我们就把这个值赋值给属性.否则我们就做默认处理.就像我们希望为Person对象的
_age
属性赋值的时候,要判断1下为_age
属性赋的值是不是在0-200之内.
否则我们就做默认处理.
-
-
如何实现这个需求(这就引出了赋值方法
setter
)-
将属性的
@public
去掉.因为一旦写上@public
就意味着外界可以直接访问对象的这个属性.(这里再强调一下,默认情况下,写在@interface
大括号内的属性外界是无法直接访问的,就是会出现一条划了线的属性名)外界一旦可以直接访问这个属性 那么外界就可以任意的为这个属性赋值 这个时候你拦都拦不住.
去掉
@public
外界就无法直接访问这个属性 也就无法赋值了. -
为类提供1个方法,这个方法专门为这个属性赋值.这个方法我们叫做
setter
a. 这个方法一定是1个对象方法 因为这个要为属性赋值.
b. 这个方法没有返回值. 因为这个方法做的事情 仅仅是为属性赋值就可以了.
c. 这个方法的名称必须以set开头. 跟上去掉下划线首字母大写的属性名.
d. 这个方法一定是有参数的. 参数的类型和属性的类型一致.参数的名称和属性的名称一致(去掉下划线)
e. 在方法的实现中,判断传入的数据是否符合逻辑.如果符合逻辑则赋值 否则做默认处理.
f. 外界想要为对象的属性赋值 那么就调用这个对象的setter方法 将要赋值的数据传入给这个方法.
方法会对这个数据进行验证 如果符合验证 就会把数据赋值给属性 否则就会做默认处理. -
这么一写,我们确实可以在为对象的属性赋值的时候做1个逻辑验证了.(这又引出了
getter
方法)但是问题就是 在外界无法取出属性的值了.
那就再写1个方法,专门用来返回属性的值.这个方法我们叫做getter方法.a. 这个方法一定是1个对象方法 因为这个方法做的事情是拿到属性的值返回.
b. 这个方法肯定有返回值 返回值的类型和属性的类型一致.
c. 这个方法的名称直接就是属性的名称(去掉下划线的)
d. 这个方法没有参数
e. 这个方法的实现是直接将属性的值返回.
f. 这个时候,如果外界希望得到属性的值.那么就只需要调用这个getter方法就可以了.
-
-
容易犯错的地方:
1). 属性的
@public
修饰符没有去掉.如果没有去掉 就算你的getter setter封装的再牛X 也没有N用.2). 搞不清楚对象. 判断参数的值是否符合逻辑 将参数赋值给属性.
3). getter setter 方法名极度不规范。
-
什么时候需要为属性封装setter和getter?
1). 只要属性需要被外界访问.就要为这个属性封装setter和getter。
哪怕在赋值或者取值的时候没有任何逻辑验证.
2). 如果属性只在类的内部访问.那么就不需要为其封装getter setter
-
只读封装与只写封装.
只读封装: 为属性封装的时候 只提供getter 不提供setter
只写封装: 为属性封装的时候.只提供setter 不提供getter
对象之间的关系
组合关系
依赖关系
关联关系
继承关系
-
组合关系.
1个对象是由多个对象组合起来的.比如.计算机对象. 是由主板对象、CPU对象、内存对象、硬盘对象...组合起来的.
主板、内存、硬盘作为计算机对象的属性.
那么这个时候,计算机对象和主板、内存、硬盘的关系为 组合关系.
-
依赖关系
1个对象的方法的参数是另外1个对象.那么我们就说他们的关系是依赖关系.比如,B类是A类方法的参数,我们就说A类依赖于B类.
美女打电话的例子.
人类:
callWithPhone:(Phone *)phone;
我们就说人类依赖于电话类. 人要打电话 就必须要有电话对象.电话类:
耦合度: 当修改1个对象的时候 对另外1个对象的影响程度.
1个类修改了 另外1个类就玩完.
低耦合: 当修改1个对象的时候 对另外1个对象的影响较小甚至没有影响.
高内聚: 1个对象仅仅做自己相关的事情.
单一职责原则. 1个类只做自己的事情.别人的事情给别人做. -
关联关系
关联体现的是两个类之间语义级别的一种强依赖关系,
比如我和我的朋友,这种关系比依赖更强、不存在依赖关系的偶然性、关系也不是临时性的,一般是长期性 的,
而且双方的关系一般是平等的。关联可以是单向、双向的。
表现在代码层面,
为被关联类B以类的属性形式出现在关联类A中,也可能是关联类A引用了一个类型为被关联类B的全局变量。案例:人使用iPad听歌.
(也就是缺一不可,缺少一个对象,这个方法就无法执行) -
继承