OC语言基础知识

一、面向对象

  OC语言是面向对象的,C语言是面向过程的,面向对象和面向过程知识解决问题的两种思考方式,面向过程关注的是解决问题涉及到的步骤,而面向对象关注的是设计能够实现解决问题所需功能的类。

  术语:OC面向对象    OPP面向对象编程

二、类

(一)关于类

  1>类是一个抽象的概念,而对象是一个真实存在的物体,一个对象包括两部分:属性跟方法

  2>类的设计最重要的三点:类名、  属性  、方法

  注意:一般名词都是类,拥有相同属性和行为的对象都可以抽象为一个类,类名是标识符的一种,需要符合规范,通常类名的第一个字母大写,且不能有下划线,如果由多个单词组成则使用驼峰法则。在OC中,对象对方法的调用称为消息机制,即向既定的对象发送了什么消息。

(二)简单的内存分析

  1>类创建对象,每个对象在内存中都占据一定的存储空间,每个对象都有一份属于自己的单独的成员变量(属性或实例变量),所有的对象共用类的成员的方法,方法在整个内存中只有一份,类本身在系统中占据一份内存空间,类的方法存在于此。

  2>每个对象内部都默认有一个isa指针来指向这个对象所使用的类。

  3>[person(对象名) run(对象方法)]  表示:给person所指向的对象发送一条run消息,调用对象的run方法,此时对象会顺着内部的isa指针找到存储于类中的方法并执行。

  4>isa是对象中隐藏的指针,指向创建这个对象的类。

(三)类的声明和实现

  1> 接口部分:@interface 声明了类于父类   以@interface编译指令为开始,以@end结束

      实现部分:@implementation   ...   @end

  2>类方法以+开头,如+(void) run;

  类的声明1:

#import <Foundation/Foundation.h>

@interface Student : NSObject{
    
    /** 学号*/
    NSInteger _no;   //勿忽略下划线
    /** 姓名*/
    NSString * _name;
    /** 成绩*/
    CGFloat _score;
    
}

//*********set get 方法*************

/**
 *  学号set get方法
 *
 *  @param no 学号
 */
-(void) setNo:(NSInteger) no;
-(NSInteger) no;

/**
 *  姓名方法
 *
 *  @param name 姓名
 */
-(void) setName:(NSString *) name;
-(NSString *) name;

/**
 *  成绩方法
 *
 *  @param score 成绩
 */
-(void) setScore:(CGFloat) score;
-(CGFloat) score;


//*************其他方法***************

/**
 *  查询成绩方法
 */
-(void) showScore;

/**
 *  补考方法
 */
-(void) reExam;
@end

以上声明可以用@property简化如下:

#import <Foundation/Foundation.h>
@interface Student : NSObject

/** 学号*/
@property NSInteger no;     //无需加下划线

/**姓名*/
@property NSString * name;

/** 成绩*/
@property CGFloat score;

//*************其他方法***************

/**
 *  查询成绩方法
 */
-(void) showScore;

/**
 *  补考方法
 */
-(void) reExam;
@end

类的实现1:

#import "Student.h"

@implementation Student


#pragma mark - 学号
-(void) setNo:(NSInteger) no{
    
    _no=no;
    
}
-(NSInteger) no{
    
    return _no;
    
}

#pragma mark - 姓名
-(void) setName:(NSString *) name{
    
    _name=name;
    
}
-(NSString *) name{
    
    return _name;
}

#pragma mark - 成绩
-(void) setScore:(CGFloat) score{
    
    _score=score;
    
}
-(CGFloat) score{
    
    return _score;
    
}


//*************其他方法***************

#pragma mark - 查询成绩
-(void) showScore{
    
    NSLog(@"成绩:%g分",_score);
    
}

#pragma mark - 补考
-(void) reExam{
    
    if (_score<60) {
        NSLog(@"补考");
    }else{
        NSLog(@"无需补考");
    }
}
@end

类的调用:在主函数创建一个Student类型的对象(先调用alloc分配存储空间,然后调用init方法初始化为0),并定义了一个Student类型的指针指向创建的这个对象,之后用.语法初始化对象变量score的值为59,然手调用对象的reExam方法.

#import <Foundation/Foundation.h>
#import "Student.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
       
        //初始化并赋值
        Student * stu=[[Student alloc] init];
        stu.score=59;
        
        //调用方法
        [stu reExam];
        
    }
    return 0;
}

练习题

说明:

属性: 坐席数量 剩余数量 票据信息,其中票据信息又包括电影名称 电影类型 开始时间 价格,实现当前上映电影的简介以及卖票,退票功能

解析:票据信息Ticket需要新建一个类,并且将套入到Cinema类中

Cinema类的声明

#import <Foundation/Foundation.h>
#import "Ticket.h"
@interface Cinema : NSObject

/** 坐席数量*/
@property NSInteger seatNo;

/** 剩余数量*/
@property NSInteger leftNo;

/** 票据信息*/
@property Ticket * ticket;

//************其他方法************

/**
 *  卖票
 */
-(void) sellTickets;

/**
 *  退票
 */
-(void) returnTickets;

@end

Ticket类的声明

#import <Foundation/Foundation.h>

@interface Ticket : NSObject

/** 电影名称*/
@property NSString * name;

/** 电影类型*/
@property NSString * type;

/** 开始时间*/
@property NSString * startTime;

/** 价格*/
@property CGFloat price;


//***************其他方法************

/**
 *  展示电影简介
 */
-(void) showFilm;

@end

Cinema类的实现

#import "Cinema.h"
@implementation Cinema

#pragma mark - 卖票
-(void) sellTickets{
    
    //嵌套方法
    [_ticket showFilm];
    
    //卖票
    _leftNo-=1;
    NSLog(@"现有%ld张票,成功卖票1张,还剩%ld张",_seatNo,_leftNo);
    
}

#pragma mark - 退票
-(void) returnTickets{
    
    //嵌套方法
    [_ticket showFilm];
    
    //退票
    _leftNo+=1;
    NSLog(@"现有%ld张票,成功退票1张,还剩%ld张",_seatNo,_leftNo);

}
@end

Ticket类的实现

#import "Ticket.h"

@implementation Ticket

#pragma mark - 展示电影简介
-(void) showFilm{

    NSLog(@"
电影名称:%@
电影类型:%@
开始时间:%@
价格:%g",_name,_type,_startTime,_price);
    
}

@end

调用

#import <Foundation/Foundation.h>
#import "Cinema.h"
#import "Ticket.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
       /*
        //初始化并赋值
        Student * stu=[[Student alloc] init];
        stu.score=59;
        
        //调用方法
        [stu reExam];
        */
        
        //初始化并赋值
        Cinema * cinema=[[Cinema alloc] init];
        cinema.seatNo=50;
        cinema.leftNo=50;
        
        Ticket * ticket=[[Ticket alloc] init];
        ticket.name=@"极限挑战";
        ticket.type=@"娱乐搞笑类";
        ticket.startTime=@"20:35";
        ticket.price=65.5;
        
        //调用
        [cinema sellTickets];
        [cinema returnTickets];
        
    }
    return 0;
}

(四)常见错误

  1>@interface  @end 和 @implementation @end不能嵌套包含

  2>只有类的声明,却没有类的实现

  3>漏写@end

  4>两个类的声明嵌套(可以把顺序打乱)

  5>成员变量没有写在{}内

  6>方法的声明写在了{}内

  7>在对类的成员变量声明的同时就给初始化了,请注意成员变量不能脱离对象而独立存在  

  8>方法无法像函数那样调用

  9>成员变量和方法不能用static等关键字修饰,不要跟C语言混淆

  10>类的实现可以写在main函数后面,在使用之前声明即可

  4>屏幕输出:NSLog(@“hello”);//相比C语言的printf会增加时间跟进程信息,并且会自动换行

三、OC对象与函数

  OC对象与函数有着本质的区别:

  1>方法的实现只能写在@implementation  ... @end中,对象方法的声明只能写在@interface  ... @end内

  2>对象方法都已-开头,类方法都以+开头

  3>对象方法只能由对象来调用,类方法只能由类来调用,不能当做函数一样调用

  4>函数属于整个文件,可以写在文件中的任何位置,包括@implementation ... @end中,但写在@interface  ...@end内会无法识别,函数的声明可以在main函数的内部,也可以在main函数的外部

  5>函数调用不依赖与对象

  6>函数内部不能直接通过成员变量名访问对象的成员变量

四、类和方法的设计

  1>工具类:基本没有任何成员变量,里面的方法基本都是类方法

  2>类方法以+开头,如+(void) run;

  3>类方法只能由类来调用

  4>调用格式:[类名 类方法名],直接在主程序中调用,无需像对象方法那样先初始化

  5>类方法的好处跟适用场合:

  不依赖与对象,执行效率更高

  能用类方法解决的问题,尽量使用类方法

  场合:当方法内部不需要使用到成员变量时,可以改为类方法

练习题

要求:设计一个工具类:一个计算器类,要求1>返回π  2>计算两个整数的和  3>计算某个整数的平法

 计算器类的声明

#import <Foundation/Foundation.h>

@interface Calculator : NSObject

/**
 *  π的类方法
 *
 *  @return π的值
 */
+(CGFloat) pi;

/**
 *  两个整数的和的类方法
 *
 *  @param num1 第一个整数
 *  @param num2 第二个整数
 *
 *  @return 返回两个整数的和
 */
+(NSInteger) sumOfNumber1:(NSInteger) num1 andNumber2:(NSInteger) num2;
/**
 *  计算一个整数的平方
 *
 *  @param no 一个整数
 *
 *  @return 返回这个整数的平方
 */
+(NSInteger) square:(NSInteger) no;

@end

计算器类的实现

#import "Calculator.h"

@implementation Calculator

#pragma mark - π
+(CGFloat) pi{

    return 3.14;
    
}

#pragma mark - 计算两个整数的和
+(NSInteger) sumOfNumber1:(NSInteger) num1andNumber2:(NSInteger) num2{
return num1+num2;
    
}

#pragma mark - 计算一个整数的平方
+(NSInteger) square:(NSInteger) no{
    return no*no;
    
}
@end

主程序:直接使用类名调用类方法

#import <Foundation/Foundation.h>
#import "Calculator.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        /*
            //初始化并赋值
            Student * stu=[[Student alloc] init];
            stu.score=59;
            
            //调用方法
            [stu reExam];
            */
        /*
            //初始化并赋值
            Cinema * cinema=[[Cinema alloc] init];
            cinema.seatNo=50;
            cinema.leftNo=50;
            
            Ticket * ticket=[[Ticket alloc] init];
            ticket.name=@"极限挑战";
            ticket.type=@"娱乐搞笑类";
            ticket.startTime=@"20:35";
            ticket.price=65.5;
            
            //调用
            [cinema sellTickets];
            [cinema returnTickets];
            */
        
        //类方法直接用类名调用
        NSInteger result=[Calculator sumOfNumber1:5 andNumber2:6];
        NSLog(@"%ld",result);
        
        NSInteger result2=[Calculator square:6];
        NSLog(@"%ld",result2);
       
        CGFloat result3=[Calculator pi];
        NSLog(@"%g",result3);
    }
    return 0;
}

注意:

  1>可以允许类方法跟对象方法同名

  2>在对象方法中可以调用类方法

五、方法名

 1>不带参数的方法

  声明: 

  

  调用:

  

  2>带参数的方法

  声明:

  

  调用:

  

  注意:冒号也是方法名的一部分

  

六、文件编译

  在工作中,通常把不同类放到不同的文件中,每个类的声明和实现分开,声明写在.h头文件中,实现写在相应的.m文件中,类名是什么,文件名的前缀就是什么.

  假设有两个类,分别是Person类和Dog类,则通常有以下五个文件:

  1>Person.h     Person类的声明文件

  2>Person.m    Person类的实现文件

  3>Dog.h          Dog类的声明文件

  4>Dog.m         Dog类的实现文件

  5>Main.m        主函数(程序入口)

  在主函数以及类的声明文件中要使用#import包含相应的头文件

补充:import的两个作用:一是和include一样,完全拷贝文件的内容;二是可以自动防止文件内容的重复拷贝(即使文件被多次包含,也只拷贝一份)

  在使用命令行进行编译链接文件的时候,通常把.m文件单文件编译,然后再把多有的目标文件链接,但是在Xcode中,是把所有的.m文件都进行编译链接的,若果出现重复定义的错误,那大部分的问题根源应该就是文件内容被重复包含或者是包含.m文件所引起的.

  源文件中不论是使用import还是include,都不能包含.m或者.c文件,只能放声明,因此,在OC中通常把类拆分开来,拆分成声明和实现两个部分

提示:这也是编程思想的一种体现,可以说.h跟.m文件是完全独立的,知识为了追求更好的可读性,才要求两个文件的文件名一致,这也是把接口和实现分离,让调用者不必去关心具体的实现细节.

  Xcode的写一行编译一行,有简单的修复功能,红色是错误提示,黄色的警告信息,如果在主程序中声明了一个变量,但是这个变量没有被使用,此时也会产生警告信息,在调试程序的时候,如果发现整个页面都没报错,但是一运行就报错,那么一定死链接报错.

原文地址:https://www.cnblogs.com/my-garden/p/5427727.html