#1. 如何学习OC

  1. OC是在C语言的基础上做加法
  2. 主要学习相比于C语言新增的数据类型, 如何定义变量, 循环控制(增强for循环)以及函数, 类的概念
  3. OC文件以.m为扩展名, .h扩展名主要用于头文件
  4. OC是一门面向对象的编程语言:封装, 继承, 多态
  5. 属性生成器 @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的地址

self

  1. 类方法可以调用类方法
  2. 类方法不可以调用对象方法(没有内存分配)
  3. 对象方法可以调用类方法
  4. 对象方法和对象方法?

##2.1. super

在子类方法中使用[super 父类方法],明确的告诉程序要执行父类的方法,
super使用场合: 子类重写父类的方法的时候想保留父类的一些行为

  1. 使用super在类方法调用父类方法, 会调用父类的类方法
  2. 使用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方法
  1. 一定是对象方法(要访问成员变量)
  2. 返回值一定是void
  3. 一定以set开头, set后面跟上去掉下划线的成员名称(并且首字母大写), 参数名字为成员变量名称去掉下划线
  • get方法
    当要访问成员变量, 获取成员变量值, 约定提供一个get方法
  1. 一定是对象方法
  2. 一定有返回值, 并且返回值类型和成员变量类型一致
  3. 方法名称和成员变量去掉下划线一致,
  4. 一定没有参数!!!!
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

可以直接在其他文件中直接访问,可以在本类对象方法中直接访问, 在子类中可以访问父类的public属性

  • @private

当private,在其他文件中不能直接访问, 可以在本类对象方法中直接访问, 在子类中不可以访问父类private属性

  • @protected

protected,在其他文件中不能直接访问,可以在本类对象中直接访问, 并且默认情况下所有属性是protected的, 子类可以访问父类中protected属性

  • @package
  1. 在.h的声明在.m中实现的方法叫做公有方法
  2. 不在.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. 自定义构造方法

  1. 一定是对象方法, 以-号开头
  2. 方法名称一般以init开头
  3. 返回值一般是id
  4. 自定义的构造方法, 需要在头文件中写声明
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. 点语法仅对属性有用
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. 作为函数的返回值和函数的参数
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是在运行时动态的检查对象的类型(弱语法)