#1. 如何学习OC
- OC是在C语言的基础上做加法
- 主要学习相比于C语言新增的数据类型, 如何定义变量, 循环控制(增强for循环)以及函数, 类的概念
- OC文件以.m为扩展名, .h扩展名主要用于头文件
- OC是一门面向对象的编程语言:
封装, 继承, 多态
- 属性生成器 @property, @synthesize: 简化代码
#2. 类方法和对象方法
对象方法:
1. 以 - 号开头,
2. 只能由对象调用
3. 对象方法中可以访问成员变量(实例变量, 由内存分配)
4. 对象方法中可以调用类方法
类方法:
1. 以 + 号开头,
2. 只能由类名调用
3. 类方法中不可以访问成员变量(实例变量)
4. 类方法中不可以调用对象方法
5. 类方法中可以调用类方法(同名的除外)
同名的对象方法和类方法是通过加号和减号识别
self, 谁调用这个方法, self就代表谁
1 2 3 4 5 6 7 8 9 10 11 12 13
| - (void)test { NSLog(@"person test"); NSLog(@"self = %p", self); } Person *p1 = [Person new]; NSLog(@"p = %p", p1); [p1 test]; 打印结果, p1和self由相同的内存地址, self指向了对象的isa; 类的地址就是第一个属性的地址, 也就是isa的地址
|

- 类方法可以调用类方法
- 类方法不可以调用对象方法(没有内存分配)
- 对象方法可以调用类方法
- 对象方法和对象方法?
##2.1. super
在子类方法中使用[super 父类方法],明确的告诉程序要执行父类的方法,
super使用场合: 子类重写父类的方法的时候想保留父类的一些行为
- 使用super在类方法调用父类方法, 会调用父类的类方法
- 使用super在对象方法调用父类方法, 会调用父类的对象方法
组合模式 : xxx拥有xxx
继承模式 : xxx是xxx
##2.2. description方法
%@打印一个对象, 会调用对象description方法,这个方法来自NSObject, 则可以重写(NSString *)description; (包含+ - 两个方法)
- [ NSString stringWithFormat: @”xxx = %d”]按照自定义格式生成字符串
1 2 3 4
| - (NSString *)description { return [NSString stringWithFormat:@"age = %d, name = %@, tel = %@", _age, _name, _tel]; }
|
##2.3. set和get方法
- set方法
为了给多个成员变量赋值, 约定提供一个set方法
- 一定是对象方法(要访问成员变量)
- 返回值一定是void
- 一定以set开头, set后面跟上去掉下划线的成员名称(并且首字母大写), 参数名字为成员变量名称去掉下划线
- get方法
当要访问成员变量, 获取成员变量值, 约定提供一个get方法
- 一定是对象方法
- 一定有返回值, 并且返回值类型和成员变量类型一致
- 方法名称和成员变量去掉下划线一致,
- 一定没有参数!!!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 生成set/get的简便方法 /* @property // 编译器特性,用来自动生成成员变量的get/set方法的声明(宰Xcode4.4之前), Xcode4.4以后被增强, 既可以生成声明也可以生成实现 1. 告诉property要生成的get/set方法生命的成员变量类型是什么 2. 告诉property要生成的get/set犯法是哪个属性, 属性名称去掉下划线 3. 如果没有写成员变量, 会自动帮我们生成下划线开头的成员变量,但不在在.h处, 而是在.m处生成带下划线的成员变量 注意 : 如果想让子类集成父类的成员变量, 还是必须手动声明 */ //告诉xcode生成的成员变量的类型和名称 @property int age; //自动生成- (void)setAge : (int)age和 - (int)age 声明 //@synthesize age = _age 用来生成get/set方法的实现, 意思是给.h文件中名称叫做age的property生成实现 //如果没有明确告诉synthesize后面的age要赋值给谁, 他就会赋值给和他同名的变量 --------------------------------- 当我们手动同时实现set/get方法的时候, property就不会自动给我们生成成员变量
|
##2.4. 成员变量的作用域和OC中的私有方法
从第一个关键字到下一个关键字之前是一个关键词的作用域
可以直接在其他文件中直接访问,可以在本类对象方法中直接访问, 在子类中可以访问父类的public属性
当private,在其他文件中不能直接访问, 可以在本类对象方法中直接访问, 在子类中不可以访问父类private属性
protected,在其他文件中不能直接访问,可以在本类对象中直接访问, 并且默认情况下所有属性是protected的, 子类可以访问父类中protected属性
- 在.h的声明在.m中实现的方法叫做公有方法
- 不在.h中声明, 只在.m实现的方法叫做私有方法(
OC由于动态绑定, 并没有真正意义上的公有和私有)
##2.5. 类的本质
1 2 3 4 5 6 7 8 9
| Person类的本质: Person的代码被加载到代码区, 然后堆区生成Person类对象,由一个SEL指针, 指向代码区; 新建Person对象有一个isa指针,isa指针指向Person类对象. isa = (Class)Person(这个Person类也是一个对象) 最后在栈创建一个 p, 指向Person对象 SEL: SEL sel = @selector(test); //获取了test函数的地址,并赋值给sel Person *p = [Person new]; [p performSelector:sel]; //根据p的isa到Person类对象的sel找对应的SEL指针, 找到后,到指向的代码区找地址相同的函数进行调用.
|

##2.6. init过程
- init用于对类的成员变量进行初始化(类似C++的构造函数)
- init应用场景: 当用到组合类时, 定义一个类后, 这个类中的
成员变量类并没有被分配空间初始化, 需要传入一个分配空间的成员变量类. 此时可以重写init,在init初始化类中的成员变量类
1 2 3 4 5 6 7 8 9 10 11
| /* new : 1. 开辟存储空间 + alloc(用来开辟存储空间) 会返回一个没有初始化的对象 2. 初始化成员变量 - init 对对象中的成员变量进行初始化 3. 返回地址 */ Person *p1 = [Person alloc];//开辟存储空间,返回未初始化对象 Person *p2 = [p1 init]; //返回初始化后的对象 //常规写法: Person *p = [[Person alloc] init]; [p2 setAge:22]; NSLog(@"init age = %d", [p2 age]);
|
重写init方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| - (id)init //系统不知道创建的什么对象,所有返回万能指针 { /* 重写NSOject的init初始化方法注意点 : 1. 一定要调用super init方法, 先初始化父类 2. 一定要判断self是否为nil(如果父类初始化失败, 会返回nil) 3. 一定要返回self */ self = [super init]; if(self != nil) {//说明父类初始化成功 _age = 18; } return self; } //优化 - (id)init { if (self = [super init]) { //表达式先执行右部 _age = 30; } return self; }
|
###2.6.1. 自定义构造方法
- 一定是对象方法, 以-号开头
- 方法名称一般以init开头
- 返回值一般是id
- 自定义的构造方法, 需要在头文件中写声明
1 2 3 4 5 6 7 8 9 10 11 12
| //使每个对象初始化有自定义的不同的属性 使对象一出生就拥有指定的属性 - (id)initWithAge:(int)age { self = [super init]; if (self) { _age = age; } return self; } Person *p1 = [[Person alloc] initWithAge:20]; [p1 infor];
|
继承中的自定义构造方法的运行流程
1 2 3 4 5 6 7 8 9
| - (id)initAge:(int)age Name:(NSString *)name { self = [super initAge:age]; //直接调用父类自定义初始化 if (self) { //[self setAge:40]; property生成的是私有变量, setAge先在本类中招, 如果本类没有再去父类中找, 不推荐使用这个方法,推荐直接调用父类的自定义初始化 _name = name; } return self; }
|

##2.7. 自定义类方法
1 2 3 4 5 6 7 8 9 10 11
| //定义类方法, 创建Person //一般开发中会提供一个对象方法和一个类方法,用于创建初始化对象 //约定: 只要是用于创建对象的类方法, 方法名称和类名一致(首字母小写) //声明 + (id)person; //定义 + (id)person { //使用self, 而不是Person, 防止继承中动态绑定生成的不是子类对象 return [[self alloc] init] }
|
1 2 3 4 5 6 7 8 9 10
| //声明 + personWithAge : (int)age; //定义 + (id)personWithAge:(int)age { //设置成self, 使继承时可以使用,(动态绑定) self *p = [[self alloc] init]; // 或者直接使用self *p = [self person]; [p setAge:age]; return p; }
|
#3. 多态
多态: 某一种事物的多种形态(没有继承就没有多态)
多态体现 : 用父类类型的指针指向子类对象(Animal *a = [Dog new];狗既是动物也是狗)
父类指针指向子类对象,(多态), OC特性 : 调用方法时, 会动态检测对象的真实类型(动态绑定 : 运行时判定)
局限性: 使用父类指针调用子类特有(子类有, 父类没有)的方法, 必须进行强制类型转换
1 2 3 4 5 6 7 8 9 10 11 12
| //这个错误是因为 : (Xcode特性, 并不是OC的特性),父类并没有jiao这个方法 Animal *a1 = [Dog new]; //使用父类指针调用子类特有的方法, 必须进行强制类型转换 Dog *d1 = (Dog *)a1; [d1 jiao]; //错误写法, 动态检测a2的类型是Animal, Animal中没有jiao这个方法 Animal *a2 = [Animal new]; Dog *d2 = (Dog *)a2; [d2 jiao];
|
#4. 点语法
为了适应java程序员, 加入的一个编译器特性, 只能用来调用get set方法
点语法仅对属性有用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| //三种给属性赋值的方法 Girl *g = [Girl new]; //利用public在外部赋值, 不安全 g->_age = 12; g->_name = @"hello"; g->_tel = @"1321"; //利用set方法 [g setAge:22]; [g setName:@"王老吉"]; [g setTel:@"2421412"]; //利用点语法, 编译器编译时会自动转换为set方法 g.age = 23; g.name = @"加多宝"; g.tel = @"fuck"; NSLog(@"age = %d", g.age);//转化为[g age]方法, NSLog(@"%@", g);
|
#5. id类型(相当于NSObject *)
- 定义变量
- 作为函数的返回值和函数的参数
1 2 3 4 5 6 7 8 9 10 11 12 13
| //id == NSObject * NSObject *p1 = [Person new]; //动态绑定时, 若父类中没有子类的方法, 调用前需要先进行强制类型装换 Person *p2 = (Person *)p1; [p2 setAge: 33]; //id的本质 : typedef struct objc_object *id //如果用id接受一个对象, 调用对象特有的方法而不需要进行强制类型转换 //id是一个万能指针, 可以用来指向任何对象, 注意后面不要加* id p3 = [Person new]; [p3 setAge:44]; NSLog(@"id age = %d", [p3 age]);
|
#6. 互引用问题
如果在A类中导入B类, B类中又导入A类, 就会造成循环引用
使用@class className进行声明,只是告诉编译器这是一个类, 并未导入类的具体实现. 如果用到实现,可以在.m文件中加入头文件
- @class可以提高编译效率
#7. Xcode编译原理
cc -c main.m
cc main.o -framework Foundation
OC是在运行时动态的检查对象的类型(弱语法)