零、实例变量修饰符
/* @public 就是实例变量修饰符 @public >可以在其它类中访问被public修饰的成员变量 >也可以在本类中访问被public修饰的成员变量 >可以在子类中访问父类中被public修饰的成员变量 @private >不可以在其它类中访问被private修饰的成员变量 >可以在本类中访问被private修饰的成员变量 >不可以在子类中访问父类中被private修饰的成员变量 @protected >不可以在其它类中访问被protected修饰的成员变量 >可以在本类中访问被protected修饰的成员变量 >可以在子类中访问父类中被protected修饰的成员变量 注意: 默认情况下所有的实例变量都是protected @package >介于public和private之间的 如果是在其它包中访问那么就是private的 如果是在当前代码所在的包种访问就是public的 实例变量修饰符作用域: 从出现的位置开始, 一直到下一个修饰符出现 如果没有遇到下一个实例变量修饰符, 那么就会修饰后面所有的实例变量 */ @interface Person : NSObject { @public int _age; @private double _height; @protected double _weight; @package NSString *_name; NSString *_tel; NSString *_email; } @end
一、setter&getter
其实setter和getter就是对成员变量的封装,由于开发中一般不会给成员变量直接变为@public,不会让成员变量直接暴露给自己这个类之外的其他类别,但是又得提供了接口让其他类来访问,所以就又了setter和getter方法,我们可以在setter和getter中过滤自己不想的值,防止别人更改自己的类(如果是自己的类是可以直接给自己的类的成员变量赋值的,默认就是私有的)。
@interface Gun : NSObject { /* 由于将来我们经常需要定义一些方法来操作成员变量,而每个方法都必须有一个有意义的名称,而想名字非常难,所以就有了getter-setter方法 getter-setter方法格式和写法都是固定的,所以只要有getter-setter方法我们就不用煞费心思的去想方法名称了,解决我们起名字难问题 并且getter-setter方法还是程序员之间的一种规范,以后别人只要想给属性赋值立刻就会想到getter-setter方法,这样降低了程序员之间的沟通成本 */ int _size; //尺寸 } /* setter方法: 作用: 设置成员变量的值 格式: 1. setter方法一定是对象方法 2. 一定没有返回值 3. 一定以set开头, 并且set后面跟上需要设置的成员变量的名称去掉下划线, 并且首字母大写 4. 一定有参数, 参数类型一定和需要设置的成员变量的类型一致, 并且参数名称就是成员变量的名称去掉下划线 */ - (void)setSize:(int)size; /* getter方法: 作用: 获取成员变量的值 格式: 1. getter方法一定是对象方法 2.一定有返回值, 而且返回值一定和获取的成员变量的类型一致 3.方法名称就是获取的成员变量的名称去掉下划线 4. 一定没有参数 */ - (int)size; @implementation Gun // setter - (void)setSize:(int)size; { //成员变量以下划线开头的好处,就是可以区分局部变量和成员变量,可以过滤,如果该size是自己不想要的,则不用赋值给成员变量_size _size = size; } // getter - (int)size { return _size; }
二、点语法
如果给属性提供了getter和setter方法, 那么访问属性就又多了一种访问方式 , 点语法
点语法其实它的本质是调用了我们的setter和getter方法
点语法是一个编译器的特性, 会在程序翻译成二进制的时候将.语法自动转换为setter和getter方法
如果点语法在=号的左边, 那么编译器会自动转换为setter方法
如果点语法在=号的右边, 或者没有等号, 那么编译器就会自动转换为getter方法
p.name = @"lnj"; // 和下面这个是相等的 [p setName:@"lnj"]; p.age = 30; // 这个是setter p.height = 1.75; // 下面的点语法为gettet NSLog(@"age = %i, name = %@, height = %f", p.age, p.name, p.height)
三、继承
场景:
当前代码存在的问题,重复代码太多,可以利用继承来解决当前重复代码太多的问题,只要A类继承了B类, 那么A类就拥有了B类的所有属性和方法(对象方法和类方法)。
比如有一个手机类(Phone):
@interface Phone : NSObject { int _cpu; } // 打电话 - (void)signalWithNumber:(NSString *)number; // 发短信 - (void)sendMessageWithNumber:(NSString *)number andContent:(NSString *)content; // 品牌 + (void)brand; @end @implementation Phone - (void)signalWithNumber:(NSString *)number { NSLog(@"利用手机打电话给%@", number); } - (void)sendMessageWithNumber:(NSString *)number andContent:(NSString *)content { NSLog(@"利用手机发短信给%@, 内容是%@", number, content); } + (void)brand { NSLog(@"品牌"); } @end
一个iPhone类和Andriod继承自Phone,iPhone和Andriod就有了Phone中的所有属性和方法(类方法和对象方法),如果想扩展父类中方法可以使用super来实现父类方法,然后再补充自己的。
@interface Iphone : Phone // 继承父类相当于将父类的属性copy了一份到子类中 /* 继承父类之后不能重写父类的属性,重复定义了 { int _cpu; } // 打电话 - (void)signalWithNumber:(NSString *)number; // 发短信 - (void)sendMessageWithNumber:(NSString *)number andContent:(NSString *)content; */ - (void)setCpu:(int)cpu; - (int)cpu; @end @implementation Iphone /* - (void)signalWithNumber:(NSString *)number { NSLog(@"利用iPhone手机打电话给%@", number); } - (void)sendMessageWithNumber:(NSString *)number andContent:(NSString *)content { NSLog(@"利用iPhone手机发短信给%@, 内容是%@", number, content); } */ - (void)setCpu:(int)cpu { _cpu = cpu; } - (int)cpu { return _cpu; } @end
四、多态
多态是建立在继承的基础上的,只有先有了继承才有多态。
父类指针指向子类对象。
优点:提高了代码的扩展性
注意点:如果父类指针指向子类对象, 如果需要调用子类特有的方法(既该方法只有子类有,父类是没有的), 必须先强制类型转换为子类才能调用。
#import "Dog.h" // 子类 #import "Cat.h" // 子类 #import "Animal.h" // 父类 #import "Person.h" #import "Pig.h" // 子类 int main(int argc, const char * argv[]) { /* Dog *d = [Dog new]; [d eat]; Cat *c = [Cat new]; [c eat]; */ /* // 多态: 事物的多种表现形态 // 动态类型: 在编译的时候编译器只会检查当前类型对应的类中有没有需要调用的方法 // 在运行时,系统会自动判断a1的真实类型(编译和运行时候不一样) Animal *a1 = [Dog new]; [a1 eat]; // 注意点: 在多态中, 如果想调用子类特有的方法必须强制类型转换为子类才能调用 // [a1 kanJia]; Dog *d1 = (Dog *)a1; [d1 kanJia]; */ Dog *d = [Dog new]; Cat *c = [Cat new]; Pig *p = [Pig new];
// 比如person中有一个方法给动物喂食物,直接传递给一个继承自Animal类型的对象(dog,cat,pig)就行,运行时候会自动甄别是属于哪个对象的,这样就简单许多了。不然在person中得创建多个给不同动物喂东西的方法,采用多态,用一个方法就搞定了。 [Person food:d]; [Person food:c]; [Person food:p]; return 0; }
五、self
self不能离开类,离开了类就没有了任何意义
self在类中:
@implementation Iphone /* 注意点: 类方法中可以直接调用类方法 类方法中不可以直接调用对象方法 类方法中不能访问成员变量,因为成员变量只能用对象来调用,类中的self代表着类 */ + (void)carameWithFlahlightStatus:(FlahlightStatus)status { if (status == kFlahlightStatusOpen) { // [Iphone openFlahlight]; // 其实在类方法中调用类方法除了可以使用类名调用以外, 还可以使用self来调用 [self openFlahlight]; }else { // [Iphone closeFlahlight]; // self == Iphone [self closeFlahlight]; } NSLog(@"拍照"); } + (void)openFlahlight { NSLog(@"打开闪光灯"); } + (void)closeFlahlight { NSLog(@"关闭闪光灯"); } @end
self在对象中:
/* 注意: 如果self在对象方法中, 那么self就代表调用当前对象方法的那个对象 如果self在类方法中, 那么self就代表调用当前类方法的那个类 总结: 我们只用关注self在哪一个方法中 , 如果在类方法那么就代表当前类, 如果在对象方法那么就代表"当前调用该方法的对象" */ - (void)carameWithFlahlightStatus:(FlahlightStatus)status { if (status == kFlahlightStatusOpen) { // 其实self不仅仅可以调用我们的类方法, 还可以调用对象方法 // self == 对象 == p NSLog(@"self = %p", self); [self openFlahlight]; }else { [self closeFlahlight]; } NSLog(@"拍照"); } - (void)openFlahlight { NSLog(@"打开闪光灯"); } - (void)closeFlahlight { NSLog(@"关闭闪光灯"); }
补充点self的注意点:
/* 注意: >self会自动区分类方法和对象方法, 如果在类方法中使用self调用对象方法, 那么会直接报错 >不能在对象方法或者类方法中利用self调用当前self所在的方法 使用场景: 可以用于在对象方法之间进行相互调用 可以用于在类方法之间进行相互调用 可以用于区分成员变量和局部变量同名的情况 */ + (void)carameWithFlahlightStatus:(FlahlightStatus)status { if (status == kFlahlightStatusOpen) { // NSLog(@"self = %p", self); [self openFlahlight]; // p }else { [self closeFlahlight]; } NSLog(@"拍照"); } + (void)openFlahlight { NSLog(@"打开闪光灯"); // NSLog(@"self = %p", self); // 死循环 [self openFlahlight]; // p } + (void)closeFlahlight { NSLog(@"关闭闪光灯"); } - (void)setCpu:(int)cpu { // 区分局部变量和成员变量 self->cpu = cpu; }