IOS开发入门(2)

继续上一节
PS:前期为了说明白IOS开发,会截取很多书上看到的内容来更详细的说明

PS:本节中是对Object-C代码的一个应用环节,没有涉及到体现在移动端上(就是像上一节那样在模拟器上显示Hello World)

先来介绍一下类和对象

对象是面向对象编程的核心。可以通过构建类定义对象,类即为创建对象的模板。

在Objective-C中实现类与C语言类似,需要分两处进行:头文件和实现文件(具体的我就不细说了)

根据下面代码进行说明

#import <Foundation/Foundation.h>
@interface SimpleCar : NSObject {
  NSString *_make;
  NSString *_model;
  int _year;
}

@property float fuelAmount;

- (void)configureCarWithMake: (NSString*)make
                       model: (NSString*)model
                        year: (int)year;
- (void)printCarInfo;
- (int)year;
- (NSString*)make;
- (NSString*)model;

@end

在Objective-C,类、变量和方法采用驼峰式命名法。在Objective-C中,我们会使用identifiersLikeThis
而不是identifies_like_this。类名首字母大写,而其他的名称首字母小写,例如上面代码中,SImpleCar
以大写字母开头。变量fuelAmount采用驼峰式命名法,但是以小写字母开头

- (void)printCarInfo;

@ 符号用在特定的一些关键字中。例如上述代码中的@interface@end,这两个元素是用来话费类接口定义的开头和结尾。
@property定义变量类型是共有的,例如@property float fuelAmount;表示fuelAmount是共有的,
也就一位置fuelAmount在SimpleCar类的外部是可以使用的

Objective-C通常使用基于NSString对象的类,而不是基于字节的用char*声明类型的C字符串。因为NSString
提供的功能远远多于C字符串。

你们会注意到在有一些变量使用了下划线(_),下划线是Objective-C中区分实例变量和getter方法的一般做法,
在这里就当做是一个习惯就行(最好接受这个习惯)。

使用x=_year是直接从实例变量获得值,而x=[self year]是调用getter方法

- (int)year

可以做一些必要的计算或者在返回之前临时计算值。setter方法类似于getter,只不过是用于设置实例变量的值。当然了,setter
也可以自己重定义,这在之后用到的时候回额外说明


恩,不多说了,直接进入项目吧


首先,使用第一节中的方法创建一个CarValet应用程序项目(忘记了点此链接

现在我们已将创建好了

现在创建一个类

右击CarValet文件夹,选择New File

这里写图片描述

选择IOS里面的Cocoa Touch Class

这里写图片描述

我们把它命名为Car

这里写图片描述

这是创建完成的后的工作区

这里写图片描述

好了,现在进入代码环节,点击Car.h,进入到Car.h文件

增加代码如下

//对于纯新手的解释://表示注释 这只能注释一行  /* 被旁边两个符号包围起来的区域全是注释 */
//  Car.h
//  CarValet
//
//  Created by XXX on 2016/12/12.
//  Copyright :emoji: 2016年 XXX. All rights reserved.
//

#import <Foundation/Foundation.h>//导入Foundation框架,在这个框架中,可以找到各种东西,从数组到日期到谓词,从URL网络连接到JSON处理,还有最最重要的对象NSObject

@interface Car : NSObject {//定义Car(NSObject的子类)对象的接口
    int _year;//是汽车的生产年份,存为一个整体对象
    NSString *_make;//汽车的品牌,存为一个NSString对象
    NSString *_model;//汽车的型号
    float _fuleAmount;//汽油,存为浮点值(也就是小数)
}

//初始化新分配的对象,并设置汽车的品牌、型号、年份和油箱中的燃料
- (id)initWithMake:(NSString*)make
            model:(NSString*)model
             year:(int)year
       fuelAmount:(float)fuelAmount;
//向调试控制台打印出汽车的信息
- (void)printCarInfo;
//下面两行是一对方法,为_fuelAmount实例变量的getter和setter方法
- (float)fuelAmount;
- (void)setFuelAmount:(float)fuelAmount;
//其他私有实例变量的getter方法
- (int)year;
- (NSString*)make;
- (NSString*)model;

@end

以上是Car.h头文件的写法,现在要去实现Car的方法,点击Car.m

代码解释会在代码中的注释里面提到

//
//  Car.m
//  CarValet
//
//  Created by 黄彬杨 on 2016/12/12.
//  Copyright © 2016年 黄彬杨. All rights reserved.
//

#import "Car.h"

@implementation Car

- (id)init {//这是默认初始化
    //在超类(NSObject)上调用init方法,这保证了NSObject要求的任何初始化逻辑,在特定于Car类的初始化逻辑之前执行完毕
    self = [super init];
    if (self != nil) {//确保self实际已经初始化了,如果已经初始化了,这个对象的剩余部分将被创建
        _year = 1900;//默认值设置为1900
        _fuleAmount = 0.0f;//默认值设置为0.0f。这里的f可以去掉,但是这是为了告诉编译器这是fload值,而不是其他类型的浮点值,建议写
    }
    return self;//返回self
    //到此为止,Car对象已经被初始化了
}

- (id)initWithMake:(NSString *)make//分配新的对象,然后将每个值传入Car对象的属性中。这是一个自定义初始化
            model:(NSString *)model
             year:(int)year
       fuelAmount:(float)fuelAmount {

    self = [super init];//首先调用初类的初始化。如果成功,初始化对象的剩余部分;失败,返回nil
    if (self !=nil) {
        _make = [make copy];//为Car对象设置所有实例变量
        _model = [model copy];
        _year = year;
        _fuleAmount = fuelAmount;
    }
    return self;
}

- (void)printCarInfo {//打印信息,当_make和_model不为空的时候才能打印(空的时候就是初始化没有啥意义打印也是白打)
    if (!_make) return;
    if (!_model) return;
    //NSlog是想控制台打印的方法
    NSLog(@"Car Make: %@",_make);
    NSLog(@"Car Model: %@",_model);
    NSLog(@"Car Year:%d",_year);
    NSLog(@"Number of Gallons in Tank: %0.2f",_fuleAmount);
}

- (float)fuelAmount {
    return _year;
}

- (void)setFuelAmount:(float)fuelAmount {
    _fuleAmount = fuelAmount;
}

- (int)year {
    return _year;
}

- (NSString*)make {
    return [_make copy];
}

- (NSString*)model{
    return [_model copy];
}


@end

可能会对nil这个东西有点疑惑,可以查看一下这里

还可能对copy有点疑惑,浅显一点讲,NSString类型是指针类型,如果直接返回_make或_model那么返回的是指针,同样指向了_make或_model,假设直接把_make赋值给A,那么这时候如果更改了_make后,A也会跟着变,但是A讲道理是不能变的,这时候就使用copy,就解决了这个问题。了解更多可以查看一下这里

现在说以属性这个概念

属性可以让我们定义实例变量,并让编译器访问器方法——也就是说,可以访问(get或set)变量或信息的方法。就是上面定义的

- (float)fuelAmount {
    return _year;
}

- (void)setFuelAmount:(float)fuelAmount {
    _fuleAmount = fuelAmount;
}

- (int)year {
    return _year;
}

- (NSString*)make {
    return [_make copy];
}

- (NSString*)model{
    return [_model copy];
}

如何声明属性:

@property float fuelAmount;

这会让编译器创建一个实例变量和两个方法;

float _fuelAmount;
- (float)fuelAmount;
- (void)setFuelAmount:(float)fuelAmount;

会发现,这跟我们刚才写的方法一样啊这。

编译器会为我们生成下划线版本的变量。任何非汽车对象都必须使用getter和setter方法。变量和方法的实现是在编译时被添加的。而如果需要做一些特殊的事情,那么可以在.m文件总,实现特定的访问器犯法,这样,编译器就会使用我们的方法替代(虽然我们上面写的跟自动生成的一样吧。。。假装他们是不一样的)

  • 现在我们把Car.m中的fuelAmount、setFuelAmount、year、make和model这下方法的实现删除
  • 打开Car.h闭关移除刚才删掉的那些方法的声明
  • 修改如下代码 变成下面的样子
//
//  Car.h
//  CarValet
//
//  Created by XXX on 2016/12/12.
//  Copyright © 2016年 XXX. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface Car : NSObject {
    int _year;
    NSString *_make;
    NSString *_model;
    float _fuleAmount;
}

@property int year;
@property NSString *make;
@property NSString *model;
@property float fuelAmount;


- (id)initWithMake:(NSString*)make
            model:(NSString*)model
             year:(int)year
       fuelAmount:(float)fuelAmount;

- (void)printCarInfo;
@end

PS 虽然我们删除了

- (float)fuelAmount {
    return _year;
}

- (void)setFuelAmount:(float)fuelAmount {
    _fuleAmount = fuelAmount;
}

- (int)year {
    return _year;
}

- (NSString*)make {
    return [_make copy];
}

- (NSString*)model{
    return [_model copy];
}

在.m里面也没看到其他的器方法,因为我们定义了

@property int year;
@property NSString *make;
@property NSString *model;
@property float fuelAmount;

所以在编译的时候会自动生成setter和getter方法(如果我们没有自己定义的话)

现在介绍一下点表示法

点表示法允许不用方括号,就可以访问对象信息。可以使用myCar.year代替[myCar year]调用,读取year实例变量的值。看起来好像是直接访问,但是实际上不是,假设一个Car对象为my。那么my.year会自己调用[myCar year],这样有利于代码维护和可读性。

例如
用点表示法

myTableViewCell.textLabel.text=@"Hello World";

如果不用的话

[[myTableViewCell textLabel] setText:@"Hello World"];

好了,更新完代码后,Car.m中打印信息的代码也是需要更新的

- (void)printCarInfo {
    if (self.make && self.model) {
        NSLog(@"Car Make: %@",self.make);
        NSLog(@"Car Model: %@",self.model);
        NSLog(@"Car Year:%d",self.year);
        NSLog(@"Number of Gallons in Tank: %0.2f",self.fuelAmount);
    } else {
        NSLog(@"Car undefined: no make or model specified");
    }
}
这里要注意一下,有了点表示法,我们可能会在自定义的getter和setter方法中使用,但这是有风险的,最好使用“_”的版本。理由如下:
- (void) setMake:(NSString*)newMake {
    if (![newMake isEqualToString:self.make) {
        self.make = newMake;
    }
}

这段代码是make属性的自定义setter,它检查新的make值是否与旧的make值相同,如果不同,将汽车对象的make属性设为新值牡丹石此处有一些隐藏的问题。

self.make = newMake;

可以解释为:

[self setMake:newMake];

结果即为对相同setter方法的递归调用,然后无限递归,最后程序爆炸(崩溃),正确的做法是在setter方法中使用下划线版本的变量,改为

_make = newMake;

保险的做法是让setter和getter使用它们设置或返回的实例变量的下划线版本,任何init方法或自定义初始化方法也应该使用下划线版本。

OK,现在我们把方法定义完了,也把如何实现完成了,现在开始调用吧。

打开ViewController.m文件,在最后一条import语句的下方位置,添加

#import "Car.h"

在viewDidLoad方法下面添加如下代码

- (void)viewWillAppear:(BOOL)animated {//viewWillAppear:会在ViewController的视图每次即将在屏幕上显示时被调用。
    [super viewWillAppear:animated];

    Car *myCar = [[Car alloc] init]; //myCar被分配,并且被初始化为Car的一个实例
    [myCar printCarInfo];

    myCar.make = @"Ford";
    myCar.model = @"Escape";
    myCar.year = 2016;
    myCar.fuelAmount = 10.0f;

    [myCar printCarInfo];

    //采用自定义初始化
    Car *otherCar = [[Car alloc] initWithMake:@"Honda"
                                       model:@"Accord"
                                        year:2010
                                  fuelAmount:12.5f];

    [otherCar printCarInfo];
}

运行结果如下
这里写图片描述

在这里可能会想为什么不用点方法打印

下面是点方法
这里写图片描述
虽然也能打印出来,但是会有警告

个人理解点引用是对类的成员变更的引用,包括其成员的set get方法 ,[]就是类的方法使用。所以对于类里面的方法最好还是用[]来使用。

这一节就暂时到这里了

我的另一个博客站点:Arnold-你们好啊

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值