zoukankan      html  css  js  c++  java
  • 第一个OC类、解析第一个OC程序

    01第一个OC 类
    本文目录
    一、语法简介
    二、用Xcode创建第一个OC的类
    三、第一个类的代码解析
    四、添加成员变量
    五、添加方法
    六、跟Java的比较
    七、创建对象
    八、访问公共成员变量和方法
    说明:这个Objective-C专题,是学习iOS开发的前奏,也为了让有面向对象语言开发经验的程序员,能够快速上手Objective-C。如果你还没有编程经验,或者对Objective-C、iOS开发不感兴趣,请忽略。学习本专题之前,建议先学习C语言专题。
    OC是一门面向对象的语言,因此它也有类、对象、静态动态方法、成员变量的概念。这讲就来创建第一个OC的类。
    回到顶部
    一、语法简介
    1.
    在Java中,我们用1个.java文件就可以描述清楚一个类;在OC中,一般用2个文件来描述一个类:
    1> .h:类的声明文件,用于声明成员变量、方法。类的声明使用关键字@interface和@end。
    注意:.h中的方法只是做一个声明,并不对方法进行实现。也就是说,只是说明一下方法名、方法的返回值类型、方法接收的参数类型而已,并不会编写方法内部的代码。
    2> .m:类的实现文件,用于实现.h中声明的方法。类的实现使用关键字@implementation和@end。
     
    2.方法
    1> 方法的声明和实现,都必须以 + 或者 - 开头
    • + 表示类方法(静态方法)
    • - 表示对象方法(动态方法)
    2> 在.h中声明的所有方法作用域都是public类型,不能更改
     
    3.成员变量
    成员变量的常用作用域有3种:
    1> @public 全局都可以访问
    2> @protected 只能在类内部和子类中访问
    3> @private 只能在类内部访问
    比Java少了一种作用域:包权限作用域,原因很明显:OC没有包名的概念。
     
    回到顶部
    二、用Xcode创建第一个OC的类
     1.右击项目文件夹或者文件,选择"New File"
     
     
    2.选择Cocoa的"Objective-C class"
     
     
    3.输入类名和选择父类
     
    这里的类名为Student,父类是NSobject
     
    4.创建完毕后,项目中多了两个文件
     
    * Student.h是类的声明文件,Student.m是类的实现文件
    * 默认情况下,这2个文件的文件名跟类名一致
    * 编译器只会编译.m文件,并不会编译.h文件
     
    回到顶部
    三、第一个类的代码解析
    1.Student.h - 类的声明文件
    1#import <Foundation/Foundation.h>
    23@interface Student : NSObject
    45@end
    1> 看第3行,OC中使用关键字@interface来声明一个类,@interface后面紧跟着类名Student。
    2> 类名Student后面的冒号":"表示继承,即第3行代码的意思是Student继承自NSObject。
    3> 因为NSObject被声明在Foundation.h中,所以在第1行用#import包含了Foundation.h文件。
    4> 第5行的@end表示类的声明结束了。@interface和@end是配套使用的。
     
    2.Student.m - 类的实现文件
    1#import"Student.h"23@implementation Student
    45@end
    1> 看第3行,OC中使用关键字@implementation来实现一个类。@implementation后面紧跟的类名,表示究竟要实现哪一个类。
    2> 因为Student这个类是声明在Student.h中的,所以在第1行用#import包含了Student.h文件。如果你不包含Student.h,第3行代码肯定报错,因为它根本不知道Student是个什么鬼东西。
    3> 第5行的@end表示类的实现结束了。@implementation和@end是配套使用的。
     
    回到顶部
    四、添加成员变量
    正常情况下,我们都是把成员变量定义在头文件中,也就是类的声明文件(.h)中
    1.Student添加一个成员变量
     
    1#import <Foundation/Foundation.h>
    23@interface Student : NSObject {
    4int age; // 年龄5}
    67@end
     
    1> 第4行定义了一个int类型的成员变量age,age的默认作用域是@protected,即可以在Student类内部和子类中访问
    2> 成员变量必须写在大括号{ }里面
     
    2.设置成员变量的作用域
    接下来给Student增加几个不同作用域的成员变量
     
     1#import <Foundation/Foundation.h>
     2 3@interface Student : NSObject {
     4int age; // 年龄 5 6@public 7int no; // 学号 8int score; // 成绩 910@protected11float height; // 身高1213@private14float weight; // 体重15}
    1617@end
     
    一共有5个成员变量,其中
    @public作用域的有:no、score
    @protected作用域的有:age、height
    @private作用域的有:weight
     
    回到顶部
    五、添加方法
    前面我们定义了一个成员变量age,它的作用域是@protected,外界不能直接访问它。为了保证面向对象数据的封装性,我们可以提供age的get方法和set方法,让外界间接访问age。接下来在Student中添加age的get方法和set方法。
    1.Student.h中声明方法
     
     1#import <Foundation/Foundation.h>
     2 3@interface Student : NSObject {
     4int age; // 年龄 5 6@public 7int no; // 学号 8int score; // 成绩 910@protected11float height; // 身高1213@private14float weight; // 体重15}
    1617// age的get方法18 - (int)age;
    1920// age的set方法21 - (void)setAge:(int)newAge;
    2223@end
     
    1> 第18行声明了age的get方法,方法名就叫做age,OC建议get方法的名字跟成员变量保持一致(如果是在Java中,就应该叫做getAge)
    2> 第18行最面的 - 表示这是一个动态方法( + 则表示静态方法)。age前面的(int)表示方法的返回值为int类型,方法的返回值和参数类型都需要用小括号()包住

    3> 第21行声明了age的set方法,前面的 - 表示动态方法,(void)表示方法没有返回值
    4> 在OC方法中,一个冒号:对应一个参数。由于第21行age的set方法接收一个int类型的参数,参数名为newAge,所以(int)newAge前面有一个冒号:

    5> 一定要记住:一个冒号:对应一个参数,而且冒号:也是方法名的一部分。因此第21行set方法的方法名是setAge:,而不是setAge
     
    再加大一下难度,假如增加一个方法可以同时设置age和height,那么就应该这样写:
    1 - (void)setAge:(int)newAge andHeight:(float)newHeight;
    * 这个方法是动态方法、没有返回值,接收2个参数,所以有2个冒号:
    * 这个方法的方法名是setAge:andHeight:
    * 其实andHeight是可以省略的,它只是为了让方法名念起来通顺一点,也让(float)newHeight前面的冒号:不那么孤单

     
    2.Student.m中实现方法
    前面已经在Student.h中声明了3个方法,接下来一一实现它们
     
     1#import"Student.h" 2 3@implementation Student
     4 5// age的get方法 6 - (int)age {
     7// 直接返回成员变量age 8return age;
     9}
    1011// age的set方法12 - (void)setAge:(int)newAge {
    13// 将参数newAge赋值给成员变量age14    age = newAge;
    15}
    1617// 同时设置age和height18 - (void)setAge:(int)newAge andHeight:(float)newHeight {
    19    age = newAge;
    20    height = newHeight;
    21}
    22@end
     
    第6行对age方法进行了实现,第12行对setAge:方法进行了实现,第18行对setAge:andHeight:方法进行了实现
     
    回到顶部
    六、跟Java的比较
    如果是在Java中,一个Student.java文件就可以搞定成员变量和方法
     
     1publicclass Student {
     2protectedint age;
     3protectedfloat height;
     4 5publicint no;
     6publicint score;
     7 8privatefloat weight;
     910/**11    * age的get方法
    12*/13publicint getAge() {
    14return age;
    15    }
    1617/**18    * age的set方法
    19*/20publicvoid setAge(int newAge) {
    21        age = newAge;
    22    }
    2324/**25    * 同时设置age和height
    26*/27publicvoid setAgeAndHeight(int newAge, float newHeight) {
    28        age = newAge;
    29        height = newHeight;
    30    }
    31 }
     
     
    回到顶部
    七、创建对象
    前面已经定义了一个Student类,成员变量和方法都有了,接下来看一下怎么使用这个类创建对象。
    由于OC程序的入口点是main函数,所以在main.m文件中演示Student类的使用。
    先上完整代码
     
     1#import <Foundation/Foundation.h>
     2#import"Student.h" 3 4int main(int argc, constchar * argv[])
     5{
     6    @autoreleasepool {
     7        Student *stu = [[Student alloc] init];
     8 9        [stu release];
    10    }
    11return0;
    12 }
     
    1.包含Student.h
    因为要用到Student这个类,所以在第2行包含了它的头文件
    #import"Student.h"
     
    2.创建对象
    1> Java中是使用关键字new来创建对象,比如new Student(),其实这句代码做了2件事:
    • 给对象分配存储空间
    • 调用Student的构造方法进行初始化
    2> OC中创建对象也需要按顺序做上面所述的2件事
    1)调用Student类的静态方法alloc分配存储空间
    Student *stu = [Student alloc];
    • OC是方法调用是用中括号[ ],方法调用者写在括号左侧,方法名写在括号右侧,中间留点空格。因此上面是调用了Student类的静态方法alloc。
    • 上面调用的alloc方法会返回分配好内存的Student对象,在等号左边用了一个指向Student类型的指针变量stu来接收这个对象,注意stu左边的*号。所有OC对象都是用指针变量来接收的,如果你不了解指针,你记住下面这点就行了:利用类名定义一个变量时,类名后面一定要带个*号。
    • alloc方法是这样声明的:
    + (id)alloc;
    可以看到,它的返回值类型是id,这个id代表任何指针类型,你可以暂时理解为:id可以代表任何OC对象,类似于NSObject *。
     
    2)调用Student对象的构造方法init进行初始化
    前面调用alloc方法返回的Student对象stu是不能正常使用的,因为仅仅是分配了内存,并没有进行初始化,接下来调用对象的init方法进行初始化
    stu = [stu init];
    看清楚了,由于init是动态方法,所以这里使用stu变量来调用,并不是使用类名来调用。init会返回已经初始化完毕的对象,再次赋值给了stu变量。这时候的Student对象stu才能正常使用。
     
    3)其实,我们最常见的做法是将alloc和init连起来使用:
    Student *stu = [[Student alloc] init];
    相信有面向对象开发经验的你一眼就能看懂了,在main.m完整代码的第7行。
     
    3.销毁对象
    由于OC不支持垃圾回收,因此当不再使用某个对象时,需要调用对象的release方法释放此对象。我们在第9行销毁了stu对象。
    [stu release];
    这个release方法在这里调用一次即可,不要觉得多调用多几次,对象就会释放地干净一点,这样做会很危险,容易造成野指针错误。
     
    4.其他
    1> 也可以调用静态方法new快速创建一个对象
    1 Student *stu = [Student new];
    23 [stu release];
    不过我们还是习惯使用alloc和init来创建对象
    2> 前面我们调用了Student的alloc、init、new方法,但是你会发现Student.h中并没有声明这些方法,为什么能够调用呢?原因很简单,这些方法都是父类NSObject的,子类当然可以调用父类的方法。
     
    回到顶部
    八、访问公共成员变量和方法
    前面已经成功创建了一个Student对象,接下来访问一下它的公共变量和方法。
     
     1#import <Foundation/Foundation.h>
     2#import"Student.h" 3 4int main(int argc, constchar * argv[])
     5{
     6    @autoreleasepool {
     7        Student *stu = [[Student alloc] init];
     8 9// 访问公共变量no10        stu->no = 10;
    1112// 调用setAge:方法设置变量age的值13        [stu setAge:27];
    1415// 调用setAge:andHeight:方法同时设置变量age和height的值16        [stu setAge:28 andHeight:1.88f];
    1718// 访问公共变量no19int no = stu->no;
    20// 调用age方法获取变量age的值21int age = [stu age];
    2223// 打印no和age的值24        NSLog(@"no is %i and age is %i", no, age);
    2526        [stu release];
    27    }
    28return0;
    29 }
     
    1.第7行创建了Student对象,第26行销毁了对象
    2.第10行和第19行访问了Student对象的公共成员变量no,如果不是公共变量,不能像这样直接访问。注意访问方式:对象->成员变量
    3.第13行调用了Student对象的setAge:方法,传入参数27修改了成员变量age的值
    4.第16行调用了Student对象的setAge:andHeight:方法,同时修改了成员变量age和height的值
    5.第21行调用了Student对象的age方法获取成员变量age的值
    6.第24行输出了age和no的值,输出结果:
    2013-04-0621:54:56.221 第一个OC程序[1276:303] no is10 and age is28
     
    02解析第一个OC程序.note
     
    新建了第一个OC程序,这讲就来解析它内部的代码。
    查看项目结构可以发现这个程序中只有一个源文件:main.m
     
    打开main.m文件,代码如下
     
     1#import <Foundation/Foundation.h>
     2 3int main(int argc, constchar * argv[])
     4{
     5 6    @autoreleasepool {
     7 8// insert code here... 9        NSLog(@"Hello, World!");
    1011    }
    12return0;
    13 }
     
    1.程序的入口点:main函数
    跟C程序一样,OC程序的入口点依然是main函数。在main.m的第3行已定义了一个main函数。
     
    2.@autoreleasepool
    在Java中,有垃圾回收机制,系统会自动回收不再使用的对象;而OC是不支持垃圾回收的,需要开发人员编写代码来释放对象所占用的内存。在第6行有个@autoreleasepool{},这是跟内存管理有关的,暂时不用理解它的意思,你只需要记住:以后的OC代码都写在@autoreleasepool的{}内部。
     
    3.NSLog
    1> 第9行的NSLog是一个日志输出函数,它可以将传入的OC字符串参数输出到控制台上。
    2> 函数参数@"Hello, Wolrd!"是一个OC字符串,并不是C语言字符串,所有OC字符串的前面都有个@。
    3> 第9行代码NSLog(@"Hello, World!");的输出结果是:
     
    红框左边的信息是NSLog自动加上去的,比如日志输出时间、项目名称等。而且NSLog输出完毕后自动换行。
    4> NSLog也可以像C语言的printf函数一样支持格式符
    1int age = 10;
    2 NSLog(@"My age is %i and height is %.2f", age, 1.55f);
    * %i表示接收整型数据
    * %f表示接收浮点型数据,%.2f表示保留2位小数
    * 输出结果:
    2013-04-0617:43:07.380 第一个OC程序[693:303] My age is10 and height is1.55
     
    4.#import
    1> #import是一个预处理指令,作用跟C语言的#include类似,都是 包含(拷贝)某个文件的内容 到 预处理指令所在的位置。
    2> 第1行的#import <Foundation/Foundation.h>表示包含Foundation框架中的Foundation.h文件。
    1) Foundation框架中包含了很多常用的类和函数,比如字符串处理类NSString、日志输出函数NSLog。它的重要程度相当于Java中的java.lang.*
    2) Foundation框架和Foundation.h文件的位置:
     
    3> 我在C语言专题第三讲说过:.h称为头文件,一般用来声明一些函数,想要使用这些函数,就必须用#include包含函数所在的头文件。我们在第9行中使用的NSLog函数存在于Foundation.h文件中,因此要先包含Foundation.h文件才能使用NSLog函数。就好像在Java中,你用import关键字引入一个类后才能正常使用这个类。
    4> 在CC++中,我们用#include来包含头文件,缺点就是同一个头文件可能被包含多次。为了解决这个问题,一般会这样写头文件:
    #ifndef _TEST_H_
    #define _TEST_H_

    /*.....*/#endif
    我们在OC中,就使用#import来包含头文件,优点是可以自动防止同一个头文件被包含多次。
    5> #import <...>表示包含系统自带的文件,#import "..."表示包含开发人员自己创建的文件
  • 相关阅读:
    模拟赛总结
    2018.04.06学习总结
    2018.04.06学习总结
    Java实现 LeetCode 672 灯泡开关 Ⅱ(数学思路问题)
    Java实现 LeetCode 671 二叉树中第二小的节点(遍历树)
    Java实现 LeetCode 671 二叉树中第二小的节点(遍历树)
    Java实现 LeetCode 671 二叉树中第二小的节点(遍历树)
    Java实现 LeetCode 670 最大交换(暴力)
    Java实现 LeetCode 670 最大交换(暴力)
    Java实现 LeetCode 670 最大交换(暴力)
  • 原文地址:https://www.cnblogs.com/GhostKZShadow/p/5105201.html
Copyright © 2011-2022 走看看