Classes 类

像其它的面向对象的语言一样,Object-C也提供创建对象的蓝本。即类。

首先我们在类中定义一些能够反复使用的属性和方法。

然后,我们实例化类,即对象,之后就能够使用属性和訪问。

Object-C和C++一样。从类的实现中抽象出了类的接口。接口中定义了类的公开的方法和属性。相应的实现代码,定义方法和属性的详细的实现。

这和我们看到的函数,分离关注点是一样的。

这张我们将学习关于类接口。实现、属性、方法已经实例化的基本的语法。我们也会介绍Object-C的内省和反射的功能。

Creating Classes 创建对象

我们将定义一个叫Car的类。他的接口在Car.h文件里,他的实如今Car.m 文件里。

这些是标准的Object-C的文件扩展名。当其它的类想使用这个类的功能时,须要使用这个类的头文件,而类的实现文件是给编译器使用的。

xcode 提供方便的创建类的模板。我们能够从 File > New > File …或者Cmd + N 快捷键创建类。在模板中从iOS > Cocoa Touch中选择Object-C类模板。之后。提示您配置一些信息:

在Class 域中填写Car。在subclass of 中选择NSobject。NSObject 类是其它全部Object-C类的父类。点击下一步,提示选择文件存储的位置。选择存储在project根文件夹下。在对话框的以下,注意选择您的project为目标project。这样是保证文件会被编译。

点击next后。您会在Xcode的project导航中看到Car.h文件和Car.m文件。假设您选择project名称,在编译资源模块中。您会发如今Car.m文件在构建路径中。不论什么您想让编译器编译的文件都必须在这个类表中。

Interfaces 接口

Car.h 文件里包括一些样板代码。从如今開始,我们改动例如以下。声明一个叫model的变量和一个叫drive的方法。

// Car.h
#import <Foundation/Foundation.h> @interface Car : NSObject {
// Protected instance variables (not recommended)
} @property (copy) NSString *model; - (void)drive; @end

通过@interface指令创建接口,后面接着类的名字和父类的名字,中间用冒号隔开。

保护类型的变量能够定时在花括号内。

大多数开发人员喜欢讲实例变量放在.m文件里,而不是头文件里。

@property 声明一个public 类型的变量,并且通过copy定义内存管理行为。这种情况下,赋值给model变量的值。是一个副本,而不是直接指向值。在属性章节我们将会讨论很多其它的细节。接下来是属性的数据类型和名字,就像普通的变量声明一样。

以- (void)开头的行。定义了一个叫drive的函数,没有參数, (void) 部分定义函数的返回值类型。在方法前面的减号,表示一个实例方法,与之相相应的是类方法。

Implementations 实现

不论什么类的实现。第一件事是引入相相应的头文件。

@implementation 和@interface是一样的,除了不须要父类。私有的变量能够定义在类名之后的括号里。

// Car.m
#import "Car.h" @implementation Car {
// Private instance variables
double _odometer;
} @synthesize model = _model; // Optional for Xcode 4.4+ - (void)drive {
NSLog(@"Driving a %@. Vrooooom!", self.model);
} @end

@synthesize 帮助我们为属性产生存取方法(getter和setter方法)。默认,getter方式是属性名(model),setter方法是set前缀加属性名首字符大写,这样比手动写每个存储器的特征方便。_model 部分定义了属性的私有实例变量名。

xcode 4.4 之后,用@property声明的属性变量,自己主动产生存取方法。假设您觉得默认的实例变量命名规则能够,您能够省略@synthesize行。

drive方法的实现和接口中有相同的方法签名。可是在签名之后。有方法调用时运行的代码。我们是通过self.model 获取model的值,而不是通过_model。这是最佳实践,利用了属性的存储方法。

仅仅有在init方法和dealloc方法中您才须要直接訪问实例变量。

self 关键是指向方法调用的实例。

除了获取属性值。还能够调用方法。在教程中您将看到非常多这种样例。

Instantiation and Usage 实例化和使用方法

不论什么想訪问类的文件,都要引入类的头文件,我们绝对不能直接訪问类的实现。

这样将违反了,分离实现和接口的目的。

所以,为了让我们类生效,例如以下改动main.m.

// main.m
#import <Foundation/Foundation.h>
#import "Car.h" int main(int argc, const char * argv[]) {
@autoreleasepool {
Car *toyota = [[Car alloc] init]; [toyota setModel:@"Toyota Corolla"];
NSLog(@"Created a %@", [toyota model]); toyota.model = @"Toyota Camry";
NSLog(@"Changed the car to a %@", toyota.model); [toyota drive]; }
return 0;
}

在使用#import运行引入头文件之后,您能够使用alloc/init模式实例化类。正如您所见,实例化分成两个步骤:首先使用alloc方法为对象分配内存,接着初始化对象。你绝对不能使用未初始化的对象。

再次强调,全部对象都作为指针存储。这也就是为什么我们使用Car *toyota。而不是 Car toyota。

为了调用Object-C对象的方法。我们将实例名和方法名,放在方括号内。用空格分开。

在方法名之后是參数。參数之前是冒号。所以您有C++、java、Python编程背景,[toyota setModel:@”Toyota Corolla”]能够转化成toyota.setModel(“Toyota Corolla”)。

toyota.setModel("Toyota Corolla");

方括号的语法可能是您不太习惯。可是我确信,在您阅读网方法章节后,您会非常习惯这种写法。

这个样例为您展示了两种获取属性的方法。

您能够使用存取方法,或者点语法。

Class Methods and Variables 类方法和变量

上面的样例定义的实例方法和属性。我们也能够定义类的方法和属性。在其它的语言中称为static方法或者属性。

类方法的声明和实例方法是一样的,除了在方法之前是加号而不是减号。加入例如以下的class方法在Car.h文件里:

// Car.h
+ (void)setDefaultModel:(NSString *)aModel;

相同,在方法的实现之前也加入加号,可是没有类变量,您能够在实现文件里定义static变量模拟。

// Car.m
#import "Car.h" static NSString *_defaultModel; @implementation Car {
... + (void)setDefaultModel:(NSString *)aModel {
_defaultModel = [aModel copy];
} @end

[aModel copy] 创建了一个參数的副本,而不是直接指向參数。这就是为什么我们在声明model变量时。使用(copy)属性。

类方法使用和实例方法一样的方括号语法。可是他必须直接从类调动。不能在实例上调用。

[toyota setDefaultModel:@”Model T”]将会报错。

// main.m
[Car setDefaultModel:@"Nissan Versa"];

“Constructor” Methods 构造方法

在Object-C中没有构造方法。然而,对象的初始化是通过init方法,在对象内存分配完毕之后。这也就是为什么对象实例化分两步:分配内存和初始化。

有一个类的初始化方法,等会我们会讨论。

init是默认的初始化方法。您也能够自己定义您自己的初始化方法。

自己定义的初始化方法,没有什么特殊的。和其它的实例方法一样。

// Car.h
- (id)initWithModel:(NSString *)aModel;

为了实现这种方法。我们要遵守初始化方法的命名规范,像initWithModel。

super关键字指向它的父类,self指向方法调用的对象。加入例如以下代码到Car.m

// Car.m
- (id)initWithModel:(NSString *)aModel {
self = [super init];
if (self) {
// Any custom setup work goes here
_model = [aModel copy];
_odometer = 0;
}
return self;
} - (id)init {
// Forward to the "designated" initialization method
return [self initWithModel:_defaultModel];
}

初始化方法总是返回对象自己的引用。假设不能初始化。返回nil.这也就是为什么我们在使用之前。总是检測他是否存在。

可是总是仅仅有一个初始化方法做这件事。其它的初始化方法,调用这个指定的初始化方法。这样当你定义多个初始化方法的时候,就省去了一些模板代码。

注意,我们在initWithModel方法中直接给_model和_odometer赋值,也仅仅有这一个地方能够这样。其它的地方。须要使用self.model和self.odometer.

Class-Level Initialization 类-初始化方法

类级别的初始化方法和init方法一样。我们能够在这里初始化类,在其它地方使用它之前。比如我们赋值_defaultModel。

比如:

// Car.m
+ (void)initialize {
if (self == [Car class]) {
// Makes sure this isn't executed more than once
_defaultModel = @"Nissan Versa";
}
}

类的初始化方法,在类使用之前,仅仅能被调用一次。这就包括他全部的子类。最佳实践是我们使用self == [Car class]确认初始化方法已经运行了。注意,在类方法中,self关键字,指向类自己,而不是实例。

Object-C不强制您标示重写方法。

接下来我们看看自己定义初始化方法的运行情况。在类第一次被使用的时候,会运行[Car initialize]方法。即将_defaultModel赋值@”Nissan Versa”。这个能够在第一个NSLog中看到。

第二个自己定义方法initWithModel输出结果能够在第二个输出中看到。

// main.m
#import <Foundation/Foundation.h>
#import "Car.h" int main(int argc, const char * argv[]) {
@autoreleasepool { // Instantiating objects
Car *nissan = [[Car alloc] init];
NSLog(@"Created a %@", [nissan model]); Car *chevy = [[Car alloc] initWithModel:@"Chevy Corvette"];
NSLog(@"Created a %@, too.", chevy.model); }
return 0;
}

Dynamic Typing 动态类型

类能够被一个对象表示,即类对象,这样我们就能够方法他们的属性。

并且能够在运行时改变类的行为,添加或者改动方法。

这是非常厉害的动态类型能力。这样您能够在不知道当前对象是什么类型,也能够调用方法和设置属性。

最简单的获取类方法的方式是调用类方法。比如,[Car class]返回一个表示类的对象。您能够将此对象传递给isMemberOfClass: 和 isKindOfClass:方法获取其它对象的信息。

// main.m
#import <Foundation/Foundation.h>
#import "Car.h" int main(int argc, const char * argv[]) {
@autoreleasepool {
Car *delorean = [[Car alloc] initWithModel:@"DeLorean"]; // Get the class of an object
NSLog(@"%@ is an instance of the %@ class",
[delorean model], [delorean class]); // Check an object against a class and all subclasses
if ([delorean isKindOfClass:[NSObject class]]) {
NSLog(@"%@ is an instance of NSObject or one "
"of its subclasses",
[delorean model]);
} else {
NSLog(@"%@ is not an instance of NSObject or "
"one of its subclasses",
[delorean model]);
} // Check an object against a class, but not its subclasses
if ([delorean isMemberOfClass:[NSObject class]]) {
NSLog(@"%@ is a instance of NSObject",
[delorean model]);
} else {
NSLog(@"%@ is not an instance of NSObject",
[delorean model]);
} // Convert between strings and classes
if (NSClassFromString(@"Car") == [Car class]) {
NSLog(@"I can convert between strings and classes!");
}
}
return 0;
}

NSClassFromString() 是还有一种获取类对象的方法。

这样能够使您在运行时获取类对象。然而不是高效的。所以最好使用类方法获取类对象。

假设您对动态类型感兴趣。您能够查看Slectors和id类型的相关内容

Summary

这个模块我们学习了创建类、初始化对象、定义初始化方法、调动类方法和变量。

了解了一点关于动态类型的知识。

Object不支持命名空间。所以Cocoa中的函数有NS、CA、AV这种前缀。避免冲突。

这个对类也是一样的。我me你建议对于特定项目的类,命名使用三个字母的前缀。比如。XYZCar。

当然我们忽略了一些重要的细节。不用操心,假设您对属性和方法还不是非常清楚。下一章我我们将近不这些漏洞,我们近距离观察 @property 指令和影响他行为的属性。

Object-C 类的更多相关文章

  1. Object对象类

    Object对象类是所有类的祖先,他是默认自动继承的 Java为什么要做一个对象类呢?对象类的目的就是归一了类型,他就是把所有的类所有的对象归纳成为 Object类型.因为对象他认为对象应该拥有一些什 ...

  2. object C—类中函数的调用

    Object C-类中函数的调用 创建,三个类.然后,在代码中调用相同名字的函数.观察他们的调用次序. @interface test : NSObject - (void)print; @end @ ...

  3. C# System.Object基类

    System.Object 基类 System.Object在.Net中是所有类型的基类,任何类型都直接或间接地继承自System.Object.没有指定基类的类型都默认继承于System.Objec ...

  4. 浅析Object基类提供的Equals方法

    当我们去查看object.cs源代码文件的时候,会发现object基类提供了三种判断相等性的方法.弄清楚每种方法存在的原因,也就是具体解决了什么问题,对我们理解.net判断对象相等性的逻辑很有帮助,下 ...

  5. 2019-1-19 object祖宗类的equals重写

    package com.test; /** * object祖宗类的equals重写 * @author Mr.kemi *2019-1-19 */ public class Equals { pri ...

  6. C++ Object实体类

    *暂未完成,因为无尽BUG滚滚来. 好长时间没写完,一是能力不够,二是我还得给老板写WEB的代码.可是我不会WEB!js和PHP简直就是世界上最好的语言,因为它们能够让人更快地进入极乐世界. 让我写一 ...

  7. System.Object 基类

    System.Object在.Net中是所有类型的基类,任何类型都直接或间接地继承自System.Object.没有指定基类的类型都默认继承于System.Object. 基类特性 正由于所有的类型都 ...

  8. 深入JavaScript对象(Object)与类(class),详细了解类、原型

    JavaScript基于原型的对象机制 JavaScript原型上的哪些事 一.JavaScript基于原型的对象机制 JavaScript对象是基于原型的面向对象机制.在一定程度上js基于原型的对象 ...

  9. 【Java】【常用类】Object 基类 源码学习

    源码总览: 有好些都是native本地方法,背后是C++写的 没有关于构造器的描述,默认编译器提供的无参构造 https://blog.csdn.net/dmw412724/article/detai ...

  10. 重写用户模型时报错AttributeError: type object ‘自定义类’ has no attribute ‘USERNAME_FIELD’

    view中导入:from django.contrib.auth.models import AbstractBaseUser settings.py中设置了:AUTH_USER_MODEL='app ...

随机推荐

  1. python2.7运行报警告:UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal解决办法

    1. 程序源代码报错部分: #选择年级if grade == '幼升小': outline.nianji().pop(0).click()elif grade == "一年级": ...

  2. Python基础-week03 集合 , 文件操作 和 函数详解

    一.集合及其运算 1.集合的概念 集合是一个无序的,不重复的数据组合,它的主要作用如下 *去重,把一个列表变成集合,就自动去重了 *关系测试,测试两组数据之前的交集.并集.差集.子集.父级.对称差集, ...

  3. HTML 长文本换行

    word-break 属性指定单词在到达行尾时应如何中断. p.a { word-break: break-all; } word-break: normal|break-all|keep-all|b ...

  4. python中 in, any 和 all用法

    in if x == 1 or y == 1 or z == 1: print('passed') if 1 in (x, y, z): print('passed') any if x or y o ...

  5. cookie和session机制区别

    会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话.常用的会话跟踪技术是Cookie与Session.Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端 ...

  6. libcmt.lib和msvcrt.lib冲突,原因和解决方法

    libcmt.lib和msvcrt.lib冲突,原因和解决方法 https://blog.csdn.net/longlijun/article/details/7331093 libcmt.lib是w ...

  7. g2o初始化一些

    今天看了一下智能指针的东西,发现更简单的思路: 就是Block和solver构造时,需要传递unique_ptr,那我们将普通指针转换成unique_ptr不就可以了么: // 初始化g2o //第一 ...

  8. 平滑升级nginx

    平滑升级nginx版本技术文档 作者 联系方式 日期 版本号 马坤 852115346@qq.com 2017-12-31 V1.0.0 备注:作者水平有限,难免出现错误.如若发现错误,请您及时与作者 ...

  9. Docker:架构分解

    17分钟快速浏览一遍 Docker内部构建 要理解Docker内部构建,需要理解以下三种部件: Docker镜像(Image) Docker容器(Container) Docker仓库(reposit ...

  10. COUNT多列,但是每列都是不同条件的,怎么用一句SQL写?

    原文发布时间为:2010-09-06 -- 来源于本人的百度文章 [由搬家工具导入] 《转》http://www.cnblogs.com/ruanzuzhang/archive/2009/02/22/ ...