在开发IOS应用中,当你编写自定义的类时,你会发现很多的特殊场合。当你需要把自定义的行为和数据包装在一起时,自定义的类非常有用。在自定义的类中,你可以定义自己的存储、处理和显示数据的方法。

例如,IOS Clock应用中的World Clock面板。table view中的单元格需要显示比标准table view单元格更多的显示内容。这就是一个很好的机会来实现一个继承了UITableViewCell类的子类,用于在给定的table view单元格中显示更多的自定义内容。如果你正在设计自定义的类,你或许给某个label添加了outlet用于在信息前显示时间,还可以在单元格的右边,在自定义的时钟前显示图片等。

本节的主要内容就是让你知道Objective-C的语法和类的结构,这样就可以完成ToDoList应用的剩余行为了。从前面章节已经讨论过的ToDoItem的设计中可以知道,自定义的类在待办列表中扮演着单独的角色。在今天的教程里,你就会实现这个类并把它添加到应用中。

类的申明与实现

Objective-C中,一个类被分为两部分:接口和实现。接口中精确的定义了外部对象如何使用类中给定类型的对象。换句话说,接口定义了类实例与外部环境之间的接口。实现类中,包含了接口中申明的所有方法的可执行代码。

对象的设计思路是隐藏内部的实现细节。在Objective-C中,接口与实现通常被分开放置在单独的文件夹中,这样,你只需要让接口对外公开就可以了。使用C语言编程时,通过定义头文件和源文件的方法来把公共的申明与代码的实现细节分开。接口文件的后缀是.h,实现文件的后缀是.m。

接口

Objective-C中,接口申明的代码如下:

  

@interface ToDoItem : NSObject

@end

上面的代码中,申明了一个名为ToDoItem的类,它的父类是NSObject。

接口中,公用的属性和行为都定义在@interface申明中。上面的代码中,子类没有指定与父类不一样的东东,因此,ToDoItem的实例可以使用的行为全都继承自NSObject类。所有的对象都会有一个最小行为,因此,所有类都默认继承NSObject(或者NSObject的子类)。

实现

Objective-C语法中,类的实现如下所示:

#import "ToDoItem.h"

@implementation ToDoItem

@end

  如果你在接口中申明了方法,你需要在实现类中实现这些方法。

存储对象数据的属性

从待办列表项要存储的数据来分析,应该显示待办事项的名称,待办事项创建的时间,和待办事项计划完成的时间。因此,在自定义的ToDoItem类中,你需要在属性中存储这些信息。

在接口文件(ToDoItem.h)中,申明这些属性,如下所示:

  

@interface ToDoItem : NSObject

@property NSString *itemName;

@property BOOL completed;

@property NSDate *creationDate;

@end

在本例中,ToDoItem类申明了三个公共的属性。除非你再申明了其它的属性,否则,其它的对象只能对这三个属性的值进行读写。

要申明只读的属性,你需要在属性前再指定限定词。例如,如果你不想其它对象改变待办事项的创建时间,那你需要 这样做:

  

@interface ToDoItem : NSObject

@property NSString *itemName;

@property BOOL completed;

@property (readonly) NSDate *creationDate;

@end

属性可以是私有(private)的,也可以是公开(public)的。当把一个属性设置为私有类型时,其它的类就不能看到和获取它。例如,如果你想跟踪一个代表了项目日期的属性,又不想其它的类获取这个信息,那你可以在类实现(ToDoItem.m)文件的#import语句之后,在类的扩展里添加该属性,如下所示:

  

#import "ToDoItem.h"

@interface ToDoItem ()

@property NSDate *completionDate;

@end

@implementation ToDoItem

@end

使用getter和setter来获取属性。getter返回了属性的值,setter改变了属性的值。访问getter和setter的常见语法是点语法。对于要读写的属性来说,你可以使用点语法来对属性进行读写。如果你有一个对象:toDoItem,它是ToDoItem类的实例,那你可以这样做:

  

toDoItem.itemName = @"Buy milk";                 //Sets the value of itemName

NSString *selectedItemName = toDoItem.itemName;  //Gets the value of itemName

方法定义了对象的行为

方法定义了对象可以做什么。方法是在类中定义的,用于执行任务的代码片断。方法可以获取定义在类中的数据,也可以使用数据来执行某个操作。

例如,要想给待办事项添加被标记为完成状态的能力,你需要在接口中添加名为markAsCompleted的方法。然后,在实现类中实现这个方法的细节,如下所示:

  

@interface ToDoItem : NSObject

@property NSString *itemName;

@property BOOL completed;

@property (readonly) NSDate *creationDate;

- (void)markAsCompleted;

@end

方法前的-号表明了该方法是一个实例方法,由对象的实例调用,方法前面是一个+号的方法是类方法,类方法由类本身调用。类方法调用的常见例子是类的构造方法,你可以使用类方法获取与类相关的共享信息。

void关键字表示方法没有返回值。上面的代码中,markAsCompleted方法没有参数。

方法参数

在方法被调用时,方法里的参数会被当作方法信息的一部分被传递。

例如,你可以修改上面的代码,把markAsCompleted方法改为有一个参数的方法,这个参数用于决定某个待办事项到底是被标记为已完成还是未完成。这样,你就可以通过这个方法对待办事项设置完整的状态(完成和未完成),而不只是设置完成状态了。

  

@interface ToDoItem : NSObject

@property NSString *itemName;

@property BOOL completed;

@property (readonly) NSDate *creationDate;

- (void)markAsCompleted:(BOOL)isComplete;

@end

如上所示,已经给markAsCompleted方法添加了一个名为isComplete的参数,参数的类型是BOOL。

如果一个方法有多个参数,那么,方法名称就会被分成几个部分,每个部分都是参数的名称。如果你想给markAsComplete方法添加参数,那该方法的申明应该如下所示:

  

- (void)markAsCompleted:(BOOL)isComplete onDate:(NSDate *)date;

注意:上述代码中,方法的名称是markAsCompleted:onDate:。在实现过程中,当方法被调用时,isComplete和date这两个名字用于获取提供的值,就好像这两个名字就是变量一样。

实现方法

方法的实现是把相关的代码放在括号内。方法的名称、参数、返回类型必须与接口中定义的一样。

下面的代码是markAsCompleted:方法的实现:

  

@implementation ToDoItem

- (void)markAsCompleted:(BOOL)isComplete {

   self.completed = isComplete;

}

@end

与属性类似,方法的类型可以是private的,也可以是public的。申明在公共接口中的公共方法可以被其它对象看见和调用,其它对象看不到公共方法的具体实现。私有的方法只是在类的内部可见,也就是说私有的方法只能在类的内部被调用。这种机制非常强大,可以保证添加的类内部行为不会被其它对象调用。

例如,如果待办事项被标记为完成,那就给completionDate属性设置当前的日期。如果被标记为未完成,就把completionDate属性设置为nil。因为更新待办事项的completionDate属性是一个内部任务,因此,最佳的做法就是写类方法。但是,又得确保其它对象不能调用这个方法——否则,其它对象就可以在任意时间去改变待办事项的completionDate属性了。基于以上的考虑,应该把这个方法设置为private。

要完成上述内容,你需要更新ToDoItem的实现,在实现中添加私有的setCompletionDate方法,这个方法由类内部的markAsCompleted:方法调用,用于在待办事项被标记为完成或未完成时,更新待办项目的completionDate属性的值。注意:你并没有给接口文件添加任何东东,这是因为你不想让其它对象看到这个方法:

  

@implementation ToDoItem

- (void)markAsCompleted:(BOOL)isComplete {

   self.completed = isComplete;

   [self setCompletionDate];

}

- (void)setCompletionDate {

   if (self.completed) {

       self.completionDate = [NSDate date];

   } else {

       self.completionDate = nil;

   }

}

@end

到现在为止,你使用ToDoItem类已经设计了一个待办事项的基本表现形式。ToDoItem用属性的方式存储了待办事项本身的信息——名称,创建日期,完成状态,该类还定义了能做的行为——把待办事项标记为完成或未完成。现在,是时候把这个类添加到应用中了。

(译)Getting Started——1.3.4 Writing a Custom Class(编写自定义的类)的更多相关文章

  1. [翻译]Writing Custom Wizards 编写自定义的向导

    Writing Custom Wizards  编写自定义的向导   You can extend FastReport's functionality with the help of custom ...

  2. [翻译]Writing Custom Report Components 编写自定义报表组件

    摘要:简单介绍了如何编写一个FastReport的组件,并且注册到FastReport中使用.   Writing Custom Report Components 编写自定义报表组件 FastRep ...

  3. Spring Enable annotation – writing a custom Enable annotation

    原文地址:https://www.javacodegeeks.com/2015/04/spring-enable-annotation-writing-a-custom-enable-annotati ...

  4. TypeScript Writing .d.ts files(编写声明文件)

    当使用扩展的JavaScript库或者插件API的时候,将需要使用声明文件(.d.ts)来描述库的类型.本文内容将包括如何编写声明文件相关的一些高级概念,然后用一些例子来展示如何将各式各样的概念与声明 ...

  5. Writing Your Own Widget(自定义组件)

    英文地址:http://dojotoolkit.org/reference-guide/1.10/quickstart/writingWidgets.html#quickstart-writingwi ...

  6. 《gradle 用户指南中文版》目录

    gradle 用户指南 版权所有©2007-2017 Hans Dockter,Adam Murdoch只要您不对这些副本收取任何费用,并且进一步规定,每个副本都包含本版权声明,无论是以印刷版还是电子 ...

  7. WPF: FishEyePanel/FanPanel - 自定义Panel

    原文:WPF: FishEyePanel/FanPanel - 自定义Panel 原文来自CodeProject,主要介绍如何创建自定义的Panel,如同Grid和StackPanel. 1) Int ...

  8. [python 译] 基于面向对象的分析和设计

    [python 译] 基于面向对象的分析和设计 // */ // ]]>   [python 译] 基于面向对象的分析和设计 Table of Contents 1 原文地址 2 引言 2.1 ...

  9. 分享书籍[writing idiomatic python ebook]

    你是不是总是觉得学了python好久,蓦然回首,总是感觉写的代码不是那么有pythonic的味道.看看别人的代码(django,webpy),再看看自己的代码,觉得就是一java-python的混合体 ...

随机推荐

  1. OTL调用存储过程/函数及注意事项

    OTL 是 Oracle, Odbc and DB2-CLI Template Library 的缩写,是一个 C++ 编译中操控关系数据库的模板库,它目前几乎支持所有的当前各种主流数据库. OTL  ...

  2. 捕获和记录SQL Server中发生的死锁

    经带在论坛上看到有人在问怎么捕获和记录死锁信息,在这里,我将自己的一些心得贡献出来,与大家分享,也请各位指正. 我们知道,可以使用SQL Server自带的Profiler工具来跟踪死锁信息.但这种方 ...

  3. RTP/RTCP、TCP、UDP、RTMP、RTSP

    OSI中的层 功能 TCP/IP协议族 应用层 文件传输,电子邮件,文件服务,虚拟终端 TFTP,FTP,HTTP,SNMP,SMTP,DNS,RIP,Telnet 表示层 数据格式化,代码转换,数据 ...

  4. Fragment与Activity传递数据

    MainActivity如下: package cc.testsimplefragment0; import android.os.Bundle; import android.app.Activit ...

  5. Android APP内存优化之图片优化

    网上有很多大拿分享的关于Android性能优化的文章,主要是通过各种工具分析,使用合理的技巧优化APP的体验,提升APP的流畅度,但关于内存优化的文章很少有看到.在Android设备内存动不动就上G的 ...

  6. HDU oj password

     #include<stdio.h> #include<string.h> main() { int m; scanf("%d",&m); ch ...

  7. 浅析SQL Server中的执行计划缓存(上)

    简介 我们平时所写的SQL语句本质只是获取数据的逻辑,而不是获取数据的物理路径.当我们写的SQL语句传到SQL Server的时候,查询分析器会将语句依次进行解析(Parse).绑定(Bind).查询 ...

  8. DataBase 之 拉链表结构设计

    一.概念 拉链表是针对数据仓库设计中表存储数据的方式而定义的,顾名思义,所谓拉链,就是记录历史.记录一个事物从开始,一直到当前状态的所有变化的信息. 在历史表中对客户的一生的记录可能就这样几条记录,避 ...

  9. CDN新应用和客户

    目前的CDN配置服务主要应用于证券.金融保险.ISP.ICP.网上交易.门户网站.大中型公司.网络教学等领域.另外在行业专网.互联网中都可以用到,甚至可以对局域网进行网络优化.利用CDN,这些网站无需 ...

  10. 【转】linux性能相关的命令和概念

    1. top命令结果解读 23:31:27:系统时间 up 8 days: 表示从开机到现在,一共运行了8天 1 user:1个用户正在使用系统 load average:系统负载,即任务队列的平均长 ...