OC学习5——类和对象

1、OC是在C语言基础上进行扩展得到的一门面向对象的程序设计语言,它也提供了定义类、成员变量和方法的基本功能。类可以被认为是一种自定义的数据类型,使用它可以定义变量,所有使用类定义的变量都是指针类型的变量,他们将会指向该类的对象。类用于描述客观世界中某一类对象的共同特征,而对象则是类的具体存在。

2、类的定义:OC中定义类分为两个步骤,接口部分和实现部分。OC学习篇之---类的定义

接口部分:定义该类包含的成员变量和方法

实现部分:为该类的方法提供实现

3、接口定义用@interface关键字,用@end表明定义结束,其中紧跟该类的一队花括号用于声明该类的成员变量,花括号后面的部分用于声明该类的方法。一般接口部分的定义格式和实现为:

@interface 类名:父类{
    //定义属性,只定义,不赋值
    属性类型 属性名;
}

//定义方法,只定义,不实现
-/+方法类型标识符 (返回值类型) 方法名:形参名  形参标签2:形参2;

@end
 1 @interface Person : NSObject{
 2     //在{}中定义属性(全局变量/实例变量)
 3     //注意:只能定义不能赋值,因为这里只是在做声明操作,不能赋值的
 4     //加上下划线,为了区分局部变量
 5     NSString *_name;//姓名,默认值是nil:空指针,可以查看定义:#define nil NULL
 6     NSInteger _age;//年龄,默认值是0
 7 }
 8 
 9 //定义方法
10 //-:实例方法,+:类方法
11 //格式:方法说明 (返回类型) 方法名 : (参数类型)参数名称
12 //注意方法名是shopping:,不是shopping,这个要注意
13 - (void)shopping:(float)price;
14 
15 //定义方法设置属性值(注意setXXX)
16 - (void)setName:(NSString *)name;
17 - (void)setAge:(NSInteger)age;
18 
19 //定义方法的获取属性值(注意不是getXXX,而是直接的属性名,因为getXXX在OC中还有其他用途),没有形参则可以省略冒号
20 - (NSString *)name;
21 - (NSInteger)age;
22 
23 //多个参数的方法定义
24 //方法名两部分组成的:setName: setAge:
25 - (void)setName:(NSString *)name setAge:(NSInteger)age;
26 //或者方法名:setName: :,后面的可以省略,但是这种方式不建议使用
27 //- (void)setName:(NSString *)name :(NSInteger)age;
28 
29 //类方法
30 //在这个方法中是无法访问属性的,因为属性是属于对象的,不是类的
31 + (Person *)newPerson;
32 @end
  • 成员变量:成员变量命名一般都是以下划线开头, 避免与局部变量冲突
  • 方法类型标识符:该标识符要么是+,要么是-,其中,+表示该方法是类方法,相当于Java中的静态方法,通过类名可以直接调用;-代表该方法是实例方法,需要通过实例对象来调用。
  • 方法返回值类型:OC中方法的返回值类型可以是OC允许的任何任何数据类型,包括基本类型、构造函数和各种指针类型。
  • 方法签名关键字:OC的方法签名关键字由方法名、形参标签和冒号组成。如果方法没有形参,则可以省略冒号,像上面的获取属性值的方法

4、类的实现部分:接口部分只是对类的成员变量和方法进行声明,并没有为方法提供实现的方法体。类的实现用@implementation关键字进行标识,用@end表示结束。上述Person类的接口定义对应的实现如下。

 1 @implementation Person  
 2   
 3 //实现方法  
 4 - (void) shopping : (float)price{  
 5     NSLog(@"%@ shopping %f",_name,price);  
 6 }  
 7   
 8 - (void)setName:(NSString *)name{  
 9     _name = name;  
10 }  
11 - (void)setAge:(NSInteger)age{  
12     _age = age;  
13 }  
14 - (NSString *)name{  
15     return _name;  
16 }  
17 - (NSInteger)age{  
18     return _age;  
19 }  
20 - (void)setName:(NSString *)name setAge:(NSInteger)age{  
21     _name = name;  
22     _age = age;  
23 }  
24 + (Person *)newPerson{  
25     return [[Person alloc] init];  
26 }  
27   
28 @end  
  • 类实现部分的类名必须和类接口部分的类名相同,用于表示这是同一个类的接口部分和实现部分。
  • 类实现部分也可以在类名之后使用“:父类”来表示继承了某个父类,但是一般没有必要,因此,通常都不会这么做。
  • 类实现部分必须为类声明部分的每个方法提供方法实现。实现部分除了实现类接口部分定义的方法之外,也可以提供额外的方法定义——这些没有在接部分定义,只在实现部分定义的方法只能在类实现部分使用。同样,实现部分也可以定义成员变量,但是该成员变量只能在实现部分使用。实现部分额外定义的成员变量和方法相当于Java中的private变量和方法。

 5、根据习惯,接口部分和实现部分分别使用两个源文件进行保存,通常接口部分在.h文件中对类进行定义和声明,而实现部分则在.m文件中进行实现,这样可以更好地实现面向对象的封装特性,只需要向外暴露接口部分以供调用,实现部分则属于该类的内部实现,对于外界而言是隐藏的。

6、对象的产生和使用:当一个对象被创建成功之后,这个对象将保存在堆内存中,OC不允许直接访问堆内存中的对象,只能通过该对象的指针变量来访问该对象。如果堆内存中的对象没有任何变量指向该对象,那么程序将无法再访问该对象,OC没有提供自动的垃圾回收机制,需要程序员自己释放该对象所占用的内存,否则就会造成内存泄漏。

 1 //类的定义放在.h文件中  
 2 //类的实现放在.m文件中     
 6   
 7 #import <Foundation/Foundation.h>   
 9 #import "Person.h"  
10   
11 int main(int argc, const charchar * argv[]) {  
12     @autoreleasepool {  
13         //创建对象  
14         //在内存中申请一块内存区域  
15         //Person *person = [Person alloc];  
16         //调用初始化方法,初始化一些默认数据  
17         //可能上面申请的内存之前有被使用过,还会有以前的数据。所以我们需要将其数据清空,这时候就会将默认值进行赋值(相当于清空操作)  
18         //person = [person init];  
19           
20         //合并  
21         //这里申请了两块内存:一个是对象指针变量person,一个是alloc申请的保存Person对象的内存  
22         //可以看出alloc/init方法是类方法、可以查看NSObject对象的定义,alloc相当于给这个对象分配内存空间,init是调用这个类的构造方法,对属性和字段进行初始化操作  
23         Person *person = [[Person alloc] init];  
24           
25         //还有另外的一种方式创建一个对象,但是这种方式不建议使用,因为这种方式相当于把上面的两个合并  
26         //有时候我们需要将上面的两个步骤进行分开操作的  
27         //Person *person1 = [Person new];  
28           
29         //设置名字  
30         [person setName:@"jaingwei"];  
31           
32         //调用实例方法shopping  
33         [person shopping:200];  
34           
35         Person *person1 = [[Person alloc] init];  
36         [person1 setName:@"huangdaoyang"];  
37         [person1 shopping:300];  
38         //同时设置两个属性的值,但是这种方式不建议使用,建议使用set方法  
39         [person1 setName:@"huangdaoyang" setAge:22];  
40           
41         //get方法调用  
42         NSString *person_name = [person name];  
43         NSLog(@"name is %@",person_name);  
44         NSString *person1_name = [person1 name];  
45         NSLog(@"name is %@",person1_name);  
46           
47         //类方法调用  
48         Person *person2 = [Person newPerson];  
49         [person2 setName:@"shixin" setAge:22];  
50         [person2 shopping:400];  
51           
52         //空指针  
53         Person *person3 = nil;  
54         //不执行任何操作,但是也不会报错空指针异常  
55         [person3 setName:@"空对象" setAge:22];          
58     }  
59     return 0;  
60 }  

7、 类的初始化方法init相当于Java中的构造方法,但是OC中没有默认的init方法,必须自己实现。此外,我们还可以进行自定义初始化方法,对对象的成员变量进行初始化赋值,但是自定义的初始化方法必须以init开头命名。具体的接口分布和实现部分的示例代码如下:

 1 @interface Person : NSObject{  
 2     NSString *_name;  
 3     NSInteger _age;  
 4 }  
 5   
 6 //自定义初始化方法,必须以init开头  
 7 - (id)initWithName:(NSString *)name withAge:(NSInteger)age;  
 8   
 9 //打印信息  
10 - (void)showInfo;  
11   
12 @end  
 1 @implementation Person  
 2   
 3 - (id)initWithName:(NSString *)name withAge:(NSInteger)age{  
 4     //固定写法,可以背下来哦~~       
 5     //调用父类的初始化方法  
 6     self = [super init];  
 7       
 8     if(self != nil){  
 9         _name = name;  
10         _age = age;  
11     }  
12       
13     return self;  
14 }  
15   
16 - (void)test{  
17     NSLog(@"test");  
18 }  
19   
20 - (void)showInfo{  
21     [self test];//调用自己的方法  
22     NSLog(@"name is %@ and age is %d",_name,_age);  
23 }  
24   
25 @end
  •  OC中提供了一个self关键字,与Java中的this关键字类似。self关键字总是指向该方法的调用者(对象或类),当self出现在实例方法中时,self代表调用该方法的对象;当self出现在类方法中时,self表示调用该方法的类。self关键字的最大的作用是让类中的一个方法访问该类中的另一个方法。此外,当形参或局部变量的变量名和成员变量的变量名相同时,可以用“self->变量名”来强行制定访问访问成员变量。此外还有super关键字则与Java中的super一样,都是指代父类。
  • id类型:OC提供了一个id类型,这个id类型可以代表所有对象的类型,也就是说,任意类的对象都可以赋值给id类型。当通过id类型的变量来调用方法时,OC将会执行动态绑定。所谓动态绑定,是指OC将会跟踪对象所属的类,它会在运行时判断该对象所属的类,并在运行时确定需要的动态调用的方法,而不是在编译时就确定要调用的方法。

 8、OC中方法的所属性主要体现在如下几个方面:

  • 方法不能独立,只能在类体中定义
  • 从逻辑上看,方法要么属于类体本身,要么属于该类的一个对象
  • 不能独立调用方法,调用方法需要使用类或对象作为调用者

9、在方法定义时我们有时候需要允许方法根据需要传递不确定个数的参数,即形参个数可变的方法在OC中是可以实现的,类似OC中的NSLog()函数就可以传入任意多个参数。如果在定义方法时,在最后一个形参名之后曾加都好和三点(,...),则表明该形参可以接受多个参数值。

为了在程序中获取多个可变的形参,需要使用到如下关键字:

  • va_list:这是一个类型,用于定义指向可变参数列表的指针变量
  • va_start:这是一个函数,该函数制定开始处理可变形参的列表,并让指针变量指向可变形参列表的第一个参数。
  • va_end:结束处理可变形参,释放指针变量。
  • va_arg:该函数返回获取指针当前指向的参数的值,并将指针移动到下一个参数。
     1 #import <Foundation/Foundation.h>
     2 
     3 @interface VarArgs:NSObject
     4 //不定义成员变量,直接定义可变参数的方法
     5 - (void) test : (NSString *) name, ...;
     6 @end
     7 
     8 @implementation VarArgs
     9 - (void) test : (NSString *) name, ...
    10 {
    11     //使用va_list定义一个argList变量,该指针指向可变参数列表
    12     va_list argList ;
    13     //如果第一个name存在才需要处理后面的参数
    14     if(name)
    15     {
    16         //由于name参数并不在列表里,因此先处理name参数
    17         NSLog(@"%@", name) ;
    18         //使用va_start方法让argList指向参数列表的第一个元素
    19         va_start(argList,name) ;
    20         //使用va_arg依次提取argList中的参数
    21         NSString * str ;
    22         while(str = va_arg(argList,id))
    23         {
    24             NSLog(@"%@", str) ;
    25         }
    26         //释放argList指针,结束提取
    27         va_end(argList) ;  
    28     }
    29 }
    30 @end
    31 
    32 int main(int argc, char * argv[])
    33 {
    34     VarArgs * va = [[VarArgs alloc] init] ;
    35     [va test:@"疯狂IOS讲义", @"疯狂iosj讲义", @"i love ios", nil] ;
    36 }
posted on 2017-08-10 17:30  mukekeheart  阅读(253)  评论(0编辑  收藏  举报