[精通Objective-C]类,接口,协议与扩展

[精通Objective-C]类,接口,协议与扩展

参考书籍:《精通Objective-C》【美】 Keith Lee

目录

  • 精通Objective-C类接口协议与扩展
    • 目录
      • 类的接口
      • 类的实现
      • 实例变量
      • 属性
      • 方法
    • 协议
    • 分类
    • 扩展

创建一个类名为Atom,继承于NSObject的类。Atom类由两个文件组成,Atom.h和Atom.m,分别为类的接口和实现。

类的接口

Atom类的接口是在头文件Atom.h中设置的,用于声明类的属性和方法。

#import <Foundation/Foundation.h>

//Atom类的父类为NSObject(大多数Foundation框架类体系中的基类),类的属性和方法都声明在@interface和@end之间。
@interface Atom : NSObject 

//声明4个类的只读属性,关于属性会在后面详细介绍
@property(readonly) NSUInteger protons;
@property(readonly) NSUInteger neutrons;
@property(readonly) NSUInteger electrons;
@property(readonly) NSString *chemicalElement;

//声明1个类的方法,在Atom.h中具体实现
-(NSUInteger) massNumber;
@end

类的实现

实现Atom类的代码在Atom.m中,用于定义类的实例变量,属性和方法。

#import "Atom.h"

//在类接口中声明的所有方法都必须在类的实现文件中定义
@implementation Atom

//自定义初始化功能需要重写父类NSObject中的init方法
-(id) init{
    if ((self = [super init])) {
        //初始化chemicalElement属性支持的实例变量
        _chemicalElement = @"None";
    }
    return self;
}

//实现massNumber方法
-(NSUInteger) massNumber{
    return 0;
}

@end

完成类的实现后,就可以在其他类中使用它了。

#import <Foundation/Foundation.h>
#import "Atom.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Atom *atom = [[Atom alloc] init];
        //输出结果为None,即在init方法中chemicalElement的初始化值
        NSLog(@"Atom chemical element name: %@", atom.chemicalElement);
    }
    return 0;
}

实例变量

实例变量值为类声明的变量,它们在相应类实例(即对象)的声明周期中存在并拥有值。当对象被创建时,系统会为实例变量分配内存,当对象被创建时,系统会为实例变量分配内存,当对象被释放时系统也会释放变量占用的内存。实例变量拥有与对象对应的作用范围与命名空间。

实例变量可以类的接口或实现部分中声明,不过在类的接口中声明会违法OOP(面向对象程序设计)的封装原则,因此,最好在类的实现部分中声明实例变量。

@implementation MyClass
{
    //实例变量可以被任何代码访问
    @public
     char myChar;
    //实例变量只能在声明它的类及其子类的实例方法中被访问
    @protected
     int myInt;
    //实例变量只能在声明它的类以及与该类类型相同的其他实例中访问
    @private
     float myFloat;
    //实例变量可以被其他类实例和函数访问,但是在其所属程序包外部会被视为私有变量
    @package
     double myDouble;
}

//对象的实例方法可以直接访问实例变量
-(void)myTest{
    myInt = 1;
}

@end

属性

尽管实例变量可以方便、直接地访问对象的状态,但是会暴露类的内部,违反OPP的封装原则,因此只应在必要时声明实例变量,更好的方式是使用属性。属性与实例变量的区别是,属性无法直接访问对象的内部状态,但提供了访问这类数据的方便机制(getter/setter方法)。

属性的常用特性:

类别 特性 描述
原子性 nonatomic 使用该特性可以在多线程并发的情况中,将访问器设置为非原子性,因而能够提供不同的结果。否则,访问器会拥有原子性,赋值和返回结果永远都会同步
设置器语义 assign 默认设置,属性的设置器方法执行简单的赋值操作
设置器语义 retain setter方法先release旧值,再retain新值,拷贝时为指针拷贝
设置器语义 copy setter方法先release旧值,再复制新值,拷贝时为值拷贝
设置器语义 strong 属性使用ARC内存管理功能时,等于retain特性
设置器语义 weak 属性使用ARC内存管理功能时,类似assign特性
可读写性 readwrite 默认设置,属性可读写
可读写性 readonly 只读属性
方法名称 getter=getterName 将getter方法重命名为新读取器的名称
方法名称 setter=setterName 将setter方法重命名为新读取器的名称

属性定义有多种方法:显式定义,通过关键字补全和自动补全。

显式定义:

//以之前的init方法为例
-(id) init{
    if ((self = [super init])) {
        //变量名称就是对应属性名称前加上一条下划线
        _chemicalElement = @"None";
    }
    return self;
}

通过关键字补全:

//通过@synthesize关键字自动生成属性定义,并创建对应的getter和setter方法
@synthesize chemicalElement;
-(id) init{
    if ((self = [super init])) {
        //编译器已根据属性名自动生成实例变量名称,不用再加上下划线
        chemicalElement = @"None";
    }
    return self;
}
//也可以自定义属性对应的实例变量名称
@synthesize chemicalElement  = element;
-(id) init{
    if ((self = [super init])) {
        element = @"None";
    }
    return self;
}

自动补全:

编译器会对没有使用关键字(如@synthesize)、不是动态生成的或没有用户编写getter和setter方法的属性补全已声明的属性和相应的实例变量。

访问属性可以用访问器方法和点语法,编译器会根据标准命名习惯自动补全访问器方法,getter方法拥有与属性相同的名称,setter方法其名称以set开头,后跟首字母大写的属性名。

//getter的访问器方法
[atom chemicalElement];
//getter的点语法
atom.chemicalElement;

//setter的访问器方法
[atom setChemicalElement:输入值];
//setter的点语法
atom.chemicalElement = 输入值;

方法

方法声明由方法类型,返回值类型和一个或多个方法代码段(包括名称,参数,参数类型)构成。

+(void) withProtons:(NSUInteger)protons neutrons:(NSUInteger)neutrons electrons:(NSUInteger)electrons;

方法的类型标识符表明了该方法是类方法还是实例方法。类方法由+(加号)表示,这表示该方法拥有类的作用范围,这意味着它使用类级的操作并且无法访问类的实例变量(除非这些变量被当做参数传给它)。实例方法由-(减号)表示,这表明该方法拥有对象的作用范围,这意味着它使用实例级的操作,并且可以直接访问对象及其父对象的实例变量(根据实例变量上设定的访问控制)。
返回值类型表明了方法返回变量的类型。返回值的类型在方法类型后面的圆括号中设置。
方法代码段由名称,参数,参数类型组成,如withProtons:(NSUInteger)protons中withProtons是名称,NSUInteger是参数类型,protons是参数。

对象(发送器)通过发送消息与其他对象(接收器)进行交互,从而调用指定的方法。以下为调用方法的例子:

[Atom withProtons:6 neutrons:6 electrons:6];

协议

使用协议声明的方法和属性可以由任何类实现。协议使Objective-C支持多重继承的概念。下面创建一个名为Writer遵循NSObject协议的协议。

#import <Foundation/Foundation.h>

@protocol Writer <NSObject>

//声明一个方法,接受这个协议的类需要实现该方法
-(void) write :(NSFileHandle *) file;

@end

在Atom.h中接收协议

#import <Foundation/Foundation.h>
#import "Writer.h"

//在Atom接口中接受Writer协议
@interface Atom : NSObject <Writer>

@property(readonly) NSUInteger protons;
@property(readonly) NSUInteger neutrons;
@property(readonly) NSUInteger electrons;
@property(readonly) NSString *chemicalElement;

-(NSUInteger) massNumber;
@end

在Atom.m中实现协议

@implementation Atom

...

-(void)write:(NSFileHandle *)file{
    NSData *data = [self.chemicalElement dataUsingEncoding:NSUTF8StringEncoding];
    [file writeData:data];
    [file closeFile];
}

@end

分类

使用分类可以在不进行子类化的情况下,为已经存在的类增加功能。分类通常用于:1.拓展其他人定义的类(即使无法访问源码);2.代替子类;3.将新类的实现代码分发给多个源文件。

创建一个拓展Atom的分类Nuclear,并声明定义一个方法。

Nuclear接口代码:

#import "Atom.h"

@interface Atom (Nuclear)

-(NSUInteger) atomicNumber;

@end

Nuclear实现代码:

#import "Atom+Nuclear.h"

@implementation Atom (Nuclear)

-(NSUInteger)atomicNumber{
    return self.protons;
}

@end

然后Atom类型对象就可以直接调用该方法。

#import <Foundation/Foundation.h>
#import "Atom.h"
#import "Atom+Nuclear.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Atom *atom = [[Atom alloc] init];
        //调用分类中方法
        NSLog(@"Atom number: %lu", atom.atomicNumber);
    }
    return 0;
}

扩展

可以将扩展视为一种匿名(即未命名的)分类。在扩展中声明的方法必须在相应类的主@implementation块中实现,无法在分类中实现。拓展与分类的区别是它能够声明实例变量和属性。以下是在Atom.m中实现扩展。

#import "Atom.h"

//声明包含一个实例变量和一个方法的扩展
@interface Atom(){
    NSUInteger sum;
}
-(NSUInteger) countSum;
@end

@implementation Atom

//可以用self调用扩展中方法
-(NSUInteger) massNumber{
    return self.countSum;
}

//实现扩展中声明的方法
-(NSUInteger)countSum{
    sum = _protons + _neutrons + _electrons;
    return sum;
}

@end
时间: 2024-12-03 04:15:10

[精通Objective-C]类,接口,协议与扩展的相关文章

Swift教程_零基础学习Swift完整实例(三)_swift基础(对象和类、枚举和结构、协议和扩展、泛型)

4.对象和类(Objects and Classes) 1.同Java一致,使用class和类名来创建一个类. 2.使用init创建一个构造方法,使用deinit创建一个析构方法,通过构造方法来初始化类实例.创建类实例同java一致,在类名后面加上()(实际是调用无参数的构造方法init(),构造方法也可以带参数).使用.来访问实例的属性和方法. [objc] view plain copy class NamedShape {       var numberOfSides: Int = 0/

objective c-C中int数组在类接口中的问题

问题描述 C中int数组在类接口中的问题 我希望能在类接口中定义一个作为实体变量的cInt数组,这样我就能通过任意方法访问它. @interface aVCofMine : UIViewController{ int[] myArray;} 还有 @interface aVCofMine : UIViewController{ int myArray[];} 但是完全没用,不知道是不是不能这样定义啊?要是能定义怎么定义? 谢谢. 解决方案 C数组需要动态分配和调整尺寸: @interface A

Swift中文教程(七)协议、扩展和泛型

Protocols and Extensions 协议(接口)和扩展 Swift使用关键字protocol声明一个协议(接口): 类(classes),枚举(enumerations)和结构(structs)都可采用协议(protocol): class SimpleClass: ExampleProtocol { var simpleDescription: String = "A very simple class." var anotherProperty: Int = 6910

Apache Storm 官方文档 —— 多语言接口协议

本文描述了 Storm (0.7.1 版本以上)的多语言接口协议. Storm 多语言协议 Shell 组件 Storm 的多语言支持主要通过 ShellBolt,ShellSpout 和 ShellProcess 类来实现.这些类实现了 IBolt 接口.ISpout 接口,并通过使用 Java 的 ProcessBuilder 类调用 shell 进程实现了执行脚本的接口协议. 输出域 输出域是拓扑的 Thrift 定义的一部分.也就是说,如果你在 Java 中使用了多语言接口,那么你就需要

《Swift开发实战》——第2章,第2.7节协议和扩展

2.7 协议和扩展 在Swift语言中,使用关键字protocol来声明一个协议.例如,如下所示的演示代码. protocol ExampleProtocol { var simpleDescription: String { get } mutating func adjust() } 在Swift语言中,类.枚举和结构体都可以实现协议.例如,如下所示的演示代码. class SimpleClass: ExampleProtocol { var simpleDescription: Strin

Entity Framework 实体框架的形成之旅--为基础类库接口增加单元测试,对基类接口进行正确性校验(10)

本篇介绍Entity Framework 实体框架的文章已经到了第十篇了,对实体框架的各个分层以及基类的封装管理,已经臻于完善,为了方便对基类接口的正确性校验,以及方便对以后完善或扩展接口进行回归测试,那么建立单元测试就有很大的必要,本篇主要介绍如何利用VS创建内置的单元测试项目进行实体框架的基类接口测试. 在采用单元测试这个事情上,很多人可能想到了NUnit单元测试工具和NMock工具进行处理,其实微软VS里面也已经为我们提供了类似的单元测试工具了,可以不需要使用这个第三方的单元测试工具,经试

Entity Framework 实体框架的形成之旅--基类接口的统一和异步操作的实现(3)

在本系列的第一篇随笔<Entity Framework 实体框架的形成之旅--基于泛型的仓储模式的实体框架(1)>中介绍了Entity Framework 实体框架的一些基础知识,以及构建了一个简单的基于泛型的仓储模式的框架:在随笔<Entity Framework 实体框架的形成之旅--利用Unity对象依赖注入优化实体框架(2)>则持续优化这个仓储模式的实体框架,主要介绍业务逻辑层的构建,以及利用Unity和反射进行动态的对象注册.本篇主要介绍基类接口的统一和异步操作的实现等方

swift 中怎么调用其他类的协议

问题描述 swift 中怎么调用其他类的协议 比如我创建了一个协议protocol 一个类 我想在这个类引用这个协议请问怎么实现? 感激不尽! 解决方案 朋友的需求有点模糊,举个protpcol的例子给朋友吧 创建协议: 遵循协议: 在实现协议的类中实现协议方法

ios-在objectiveC中两个类的协议

问题描述 在objectiveC中两个类的协议 应用中有两个类都要执行protocol协议 能不能在一个类中执行另一个类的协议,然后再反过来执行? 会不会引起错误? 解决方案 主要问题是循环依赖,你可以把协议分离出来放到每天头文件里: ClassA.h: #import <Foundation/Foundation.h> #import "ClassBProtocol.h" @interface ClassA : NSObject <ClassBProtocol>