------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
一、 Object-C简介
Object翻译为物体、目标,其实就是对象。从名字就大概能猜出Object-C与C语言的区别:Object-C就是在C的基础上,加了一层面向对象的思想和语法。(下文中Object-C简称OC)
1.面向对象的思想
学习OC需要灵活运用面向对象的思想,下面说下我个人对于面向过程与面向对象的理解。
编程的目的是为了让计算机根据程序员写好的程序完成一些我们想要的功能。而想要实现一个功能,就必定需要若干个步骤来完成。比如计算1+1=2的步骤:
1> 在内存中分配两块储存空间A、B存储两个int类型数据a、b
2> 把两个加数赋值给两个int类型数据a、b
3> 再分配一块空间C储存一个int类型数据c
4> 计算a、b的和并将结果赋值给c
5> 找到c的内存地址,并将地址中储存的数据转换成十进制输出到屏幕上
如果继续深究,我们还能把步骤分的更细。当然,想达到计算1+2的目的,我们只需要关心到编程语言这一层就够了,计算机内部是如何做内存分配、进制转换的,我们不必深究。
再举个例子:
我的程序中需要一个超级复杂的计算功能,但我数学不好,而张三的数学很好,所以我请张三来帮我写一个计算器函数,我需要做计算时,只要调用张三的计算器函数就行了,这个“计算器”有计算各种复杂运算的能力,而我并不需要关心“计算器”内部是如何实现这些功能的。
笔者的数学不太好,就写一个加法的意思一下吧 =。=
这里为了看着方便,我就把代码都写到一个文件中了。
#include <stdio.h>
int sum(int, int); // 求和函数的声明
int main()
{
int a = 10, b = 12;
int c = sum(a, b); // 调用求和函数
printf("%d与%d的和是%d\n", a, b, c);
return 0;
}
int sum(int a ,int b) // 求和函数的实现
{
return a + b;
}
输出结果为:
从上面例子中,我们应该稍稍能感受到一些面向对象的思想了。其实面向对象的思想,就是把我们需要的功能(比如加减乘除)封装到一个具化的对象(比如计算器)中,然后调用对象的这些功能来实现我们的目标。所以我认为,面向对象就是把功能封装到具体的对象中,以方便我们调用,其本质仍然是面向过程,我们只要理解了这个思想,在学习OC时,就不会陌生。
在利用面向对象思想编程时,我们要养成“万物皆对象”的习惯,即把所有主体尽可能的具化为我们生活中能听到、看到的实体对象。
比如:我想听音乐,我就要去找【手机】这个对象,因为手机具有【播放音乐】的功能;我想去郊游,我就要去找【汽车】这个对象,因为汽车具有【远行】这个功能。
2.OC的一些特点
1)OC在C语言的基础上,增加了一层最小的面向对象语法。
2)OC也是弱语法(编译器容错性高,有些错误写法只警告,不报错)
3)OC中,完全兼容C语言。可以在OC代码中混入C语言代码,甚至是C++代码。
4)可以使用OC开发Mac OS X平台和iOS平台的应用程序。
3.OC的语法简介
1)OC是在C语言的基础上开发的,所以C语言中的所有语法在OC中均可用。
2)OC的关键字大多以’@‘开头
下面是一些OC中常用的关键字,大致浏览一下即可,后面会具体介绍其用法。
@interface @implementation @end
@public @protected @private
@selector @try @catch
@throw @finally @protocol
@optional @required @class
@property @synthesize @dynamic
self super id
_cmd __block __strong
__weak
3)OC中字符串与C语言字符串的区别
OC中字符串的描述是这样的 @“这是一个字符串”, 数据类型是NSString * ,输出字符串用的函数是NSLog
下面表格总结了c语言和OC中字符串的区别:
|
c语言 |
OC |
字符串的定义 |
char *name = “jack”; char name[10] = “jack”; |
NSString *name = @”jack”; |
字符串描述 |
“这是一个字符串” |
@”这是一个字符串,注意,必须以@开头” |
屏幕输出 |
Printf(“我%d岁了\n”, 20); |
NSLog(@“我%d岁了”, 20); NSLog函数会自动在句尾换行,不用写\n |
4)数据类型 BOOL
BOOL类型的数据用来表示YES或NO,所以BOOL类型的数据只能取两个值:1和0。
从下面的代码的3、4、5行可以看出,BOOL类型的本质是char类型,但输出时,我们一般把它当做整型来输出。
实际使用过程中,3、4、5行的代码是不用我们写的,系统已经自动写好了。
#import <Foundation/Foundation.h>5)文件包含
typedef signed char BOOL;
#define YES (BOOL)1
#define NO (BOOL)0
int main()
{
BOOL isMan = YES;
NSLog(@"是不是一个男人?%d", isMan);
return 0;
}
细心的同学已经发现了,上面介绍BOOL类型的代码示例中,并没有见到我们常见的#include<stdio.h>。原因是OC中字符串NSLog函数需要调用系统自带的Foundation框架(可以暂时把框架理解为某一类函数库的一个集合)。
所以,以后我们在OC程序的开头,都要写下这一句文件包含:
#import <Foundation/Foundation.h>我们可以把 #import理解为 #include的升级版,他们的作用是一样的,都是复制某个文件的全部内容到当前文件中。
而#import的好处是可以防止重复包含文件,#import的这种特性避免了因重复包含文件而降低编译效率,所以在OC程序中,我建议全部用#import.
我们可以看下面的例子:
// 即使多次 #import Foundation.h 这个文件,它也只会被复制一次6)框架的作用
#import <Foundation/Foundation.h>
#import <Foundation/Foundation.h>
#import <Foundation/Foundation.h>
#import <Foundation/Foundation.h>
#import <Foundation/Foundation.h>
#import <Foundation/Foundation.h>
苹果官方为了提高开发者们的开发效率,提供了很多框架给开发者用,框架中包含了很多常用的API(应用编程接口),Foundation框架就是我们开发OC、iOS、Mac程序必备的框架,当然,我们也可以自己DIY属于我们自己的框架。
每个框架中都有很多头文件,想要使用某个框架的全部功能,只需要包含它的主头文件就可以了,主头文件的名称与框架名相同。
例如Foundation框架的主头文件为:Foundation.h ,所以我们包含框架头文件的格式为:
// #import <框架名/框架主头文件名.h>
// 当引用非系统自带的头文件时,注意把 < > 换成 " "
#import <Foundation/Foundation.h>
PS:你问我API是啥?我也不知道,不过感觉很腻害的样子哦。我们还是循序渐进的学习吧,目前只要对框架有个大概的认知,并且调用就可以了,不必刨根问底。初学者切记把我好学习的重点,不要花费过多时间在当前不需要也无法理解的知识上,那样会降低我们的学习效率。
4.OC程序的运行过程
1)OC程序的运行过程和C语言没有任何区别:
2)需要注意的是:
1> OC的源文件后缀名为.m ,不是.c
2> 多文件开发中,.m文件和.c文件可以一起编译。
3)终端操作命令:
1> 编译:cc -c main.m one.c two.m
2> 链接:cc main.o one.o two.o -framework Foundation (如果使用了其他框架,就在Foundation后面空格,然后继续输入用到的框架名)
3> 运行:./a.out
5.第一个OC程序
下面这个程序验证了OC程序完全兼容C语言语法。
#import <Foundation/Foundation.h>
#import <stdio.h>
int main()
{
NSLog(@"这是一个OC程序");
printf("没错,这是一个OC程序\n");
return 0;
}
二、类和对象
前面已经简单介绍了面向对象的思想,在面向对象的编程思想中,有两个名词我们会经常用到:类和对象
我们可以把【类】和【对象】的关系就像是【图纸】和【实物】的关系。在学习OC的过程中,我们要习惯把所有可能的主体具化成对象。
1.类的创建
1)举个例子:如果“飞机图纸”是一个类,那么我们就可以根据这张图纸创建出很多个飞机实体,这些飞机实体就是“飞机图纸”这个类生成的对象。
在创建类时,有两个要素:【属性】和【行为】(程序员们习惯把”行为”成为”方法”,后文我们都称之为”方法")
在图纸中,我们需要描述清楚飞机有几对机翼、飞机的长度、翼展、重量、颜色、速度、等等,这些就是飞机这个类的属性;
而飞机具有起飞、航行、降落、坠毁(虽然坠毁是被动的,但我们仍然认为坠毁这个行为的主体是飞机)等行为,这些行为(方法)我们也需要在飞机这个类中写明。
2)类的创建方法
* 类的创建分为声明和实现两部分,一般类的声明写在.h文件中,类的实现写在.m文件中。
* 类的属性不需要写实现,所以类的实现其实就是写方法的实现。方法的声明和实现方法有点象函数的声明和实现。
* 类名的声明写在@interface 和 @end 之间
让我们通过下面代码来感受一下类的声明,我创建“人”这个类作为例子
1>类的声明
//
// 文件名:Person.h
//
// 这个文件中写类的声明
#import <Foundation/Foundation.h>
// @interface标志着类的声明开始
// 这个是固定格式: @interface 类名 : 父类名 (这个父类我们后面会讲到,当前我们都默认写 NSObject)
@interface Person : NSObject
{
// 大括号里写类的属性声明
// 后面会解释@public的作用
@public
// 定义一个 _age 属性来储存人的年龄
int _age;
}
// 大括号后面写类的方法的声明
// 类的方法有两种: 1.对象方法:只有这个类创建出的对象才能调用,以减号 - 开头
// 2.类方法:只有这个类可以调用,以加号 + 开头
// 这是一个对象方法的声明
- (void)showMyAge;
// @end标志着类的声明的结束
@end
2>类的实现
* 类名的实现写在@implementation 和 @end 之间
//
// Person.m
//
// 这个文件中写类的实现,类的实现其实就是方法的实现,因为类的属性不需要写实现
// 这一句作用是把类的声明copy到这个文件中,以便写方法的实现时让系统可以找到属性的声明和方法的声明
#import "Person.h"
// @implementation标志着类的实现的开始
@implementation Person
// 在@implementation和@end之间写类的实现
// 这里写方法的实现,写法是不是根函数很像?
- (void)showMyAge
{
// 当创建出具体的Person对象后,我们就可以给对象的_age属性赋值或调用它的值
NSLog(@"我今年%d岁了", _age);
}
// @end 标志着类的实现的结束
@end
3)类的命名规范:(看完这一节之后,我们可以在回头感受一下上面的代码)
1> 类名的命名规范
* 类名的第一个字母必须是大写
* 不能有下划线
* 多个英文单词,用驼峰标识
2> 属性的命名规范
* 必须以下划线开头(行业规范,后面我们就会知道为什么这样命名)
* 多个英文单词,驼峰标识
3> 方法的命名规范
* 方法名以加号’+’或减号'-‘开头,对象方法以减号开头,类方法以加号开头。
*方法名前写返回值类型,如果需要传入参数,方法名以冒号结束,并在冒号后面描述参数类型和形参名称
*数据类型必须用括号括起来 例如:(void) 、(int) 、(NSString *)
可以看下面的例子:
// - (返回值类型)方法名前半部分:(参数类型)参数1 方法名后半部分:(参数类型)参数2;
tips:类名、属性名、方法名尽量起的有意义一些,英文不好也尽量查一下词典,用一些通俗易懂的单词来表述,这样方便程序员之间交流。
2.对象的创建
上面我们已经了解了类的创建原则和方法,下面介绍一下如何通过类来创建一个对象。
OC中,我们可以根据一个类创建出无数个对象。其实,类也是一个对象。只要是对象,我们就可以调用对象所具有的方法。
调用对象方法的语法是: [ 对象名 方法名 ];
所有类都默认有一个名叫 new 的方法,我们通过这个方法可以创建出类。
#import <Foundation/Foundation.h>上面这段代码创建除了4个Person类型的对象,然而,这些对象创建好之后,我们要如何给他们的属性赋值或者调用他们呢?
#import "Person.h"
int main()
{
// Person每次调用new方法,就创建了一个属于Person类的对象
[Person new];
[Person new];
[Person new];
return 0;
}
我们不知道这些对象的家住在哪里,当然没法找到他们。所以,下面的代码才是创建对象的有效方式:
#import <Foundation/Foundation.h>
#import "Person.h"
int main()
{
// 每次创建一个Person类型的对象,我们就用一个(Person *)类型的指针指向这个对象。
// p1 p2 这2个指针储存着2个Person类型对象的地址
Person *p1 = [Person new];
Person *p2 = [Person new];
return 0;
}
现在我们已经知道了p1、p2 这两个“人”的地址,我们是不是可以约他们出来玩耍一下了?
直接访问对象属性的方法是 指针名->_age 其实我们可以认为指针名就是对象名,因为除了指针,我们无法通过其他方式来找到对象。
//
// main.m
//
#import <Foundation/Foundation.h>
#import "Person.h"
int main()
{
// 每次创建一个Person类型的对象,我们就用一个(Person *)类型的指针指向这个对象。
// p1 p2 这2个指针储存着2个Person类型对象的地址
Person *p1 = [Person new];
Person *p2 = [Person new];
// 给p1指向的对象的_age属性赋值
p1->_age = 20;
// 给p2指向的对象的_age属性赋值
p2->_age = 23;
// 取出p1的_age属性查看一下
NSLog(@"p1的年龄是%d", p1->_age);
// 调用p1指向对象的showMyAge方法
[p1 showMyAge];
// 调用p2指向的对象的showMyAge方法
[p2 showMyAge];
return 0;
}
上面代码的执行结果是:
tips:想要在类的外面(比如main函数中)直接访问对象的属性,就必须在声明该属性时,在属性前面写上@public; 为了数据的可控性和安全性,我们一般不采用@public的方式来直接访问属性,后面的篇章会详细讲解如何间接的访问对象属性。
@interface Person : NSObject
{ //大括号里写类的属性声明
NSString *_name; // _name写在@public前面,它【不能】以 p1->_name 这种方式被访问
// 写在@public以下的属性都是公有的,可被外界直接访问的(稍后在具体解释这块知识)
@public
int _age; // _age写在@public下面,所以它能以 p1->_age 的方式被直接访问
}
@end
小结:
1.OC是弱语法,即便编译器不报错,我们也要自觉养成良好的代码习惯。
2.把面向对象思想结合到生活中,有助于理解。
3.类的声明包括属性的声明和方法的声明,类的实现其实就是方法的实现。
4.OC中,指针的作用就是储存一个对象的地址。
5.对象属性默认是被保护的,想要直接访问就必须在属性前加上 @puclic
To be continue……