本文共 5455 字,大约阅读时间需要 18 分钟。
参考书籍:《精通Objective-C》【美】 Keith Lee
创建一个类名为Atom,继承于NSObject的类。Atom类由两个文件组成,Atom.h和Atom.m,分别为类的接口和实现。
Atom类的接口是在头文件Atom.h中设置的,用于声明类的属性和方法。
#import//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#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@protocol Writer //声明一个方法,接受这个协议的类需要实现该方法-(void) write :(NSFileHandle *) file;@end
在Atom.h中接收协议
#import#import "Writer.h"//在Atom接口中接受Writer协议@interface Atom : NSObject @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#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
转载地址:http://yrslo.baihongyu.com/