1.1 创建并描述一个类
开发经常需要创建类文件,可以说在项目开发过程中,很大一部分是由类组成的,要在Object-C创建一个类,需要继承NSObject或者子类,NSObject以及子类就是Object-C语言对象的实现。
打开NSObject 的定义,可以看到其头文件中仅有一百多行代码,及定义了对象及其本方法。
@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
NSObject 其实是实现了同名协议的接口(Interface),参数只有一个isa ,指向当前所属的类,在NSObject的协议中,不通的NSObject的类,需要根据实际情况来重写部分方法。
在开发中,如何去继承一个NSObject 类呢?
下面一个简单的例子
简单的创建一个类继承与NSObject的对象后,文件结构目录上会多两个文件Person的
- 头文件.h
头文件一般都是对这个类起到一个介绍的作用,开发者可以通过头文件来获取改类的一些基本信息,
包括类的属性和方法,从而不需要关心改类是如何实现的
- 实现文件.m
实现文件的代码量会比头文件多一些,针对头文件提供的属性和方法,实现文件应该提供具体的实现,从而保证这个类被很好的使用。
打开Person.h文件内容如下
#import <Foundation/Foundation.h>
@interface Person : NSObject
@end
导入Foundation 框架。Foundation框架是系统的基于CoreFoundation 框架的封装,即常说的Cocoa 框架,包含数据集合,日期,文件的处理,KVO/KVC,Runloop以及网络通信等已系列基本功能类的集合。
在头文件导入的框架尽量不要重复,如果Person类有一个UIImage的属性headImage,则需要在头文件加入UIKit文件
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
此时加入UIKit框架后可以不需要加入#import <Foundation/Foundation.h>框架,因为UIKit框架默认包含了Foundation框架,重复的包含是无意的,并且使代码看起来并没有那么简洁了,还有Person是一个模型类,并不是展示类,导入UIKit框架,会给开发者造成困扰,通常来说第一个#import 的框架可以很明确的让开发者明白改类是属于哪一种类型,所以此时的做法应该为所需要的UIImage添加一个向前的声明
#import <Foundation/Foundation.h>
@class UIImage
@interface Person : NSObject
@property (nonatomic, strong) UIImageView *headImage;
@end
接下来我们会想为*Person这个类添加字符串,name 属性,而且是必须的,而刚才提到的headImage,可能为nil,在这种情况下为了兼容Swift 的可选类型,会在property 中加上特性的修饰符:nonnull nullable
#import <Foundation/Foundation.h>
@class UIImage
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@property (nonatomic, strong, nullable) UIImageView *headImage;
@property (nonatomic, copy, nonnull) NSString *name
@end
NS_ASSUME_NONNULL_END
如果一个类的属性很多,这样做很麻烦,可以利用系统提供的NS_ASSUME_NONNULL_BEGIN/NS_ASSUME_NONNULL_END 默认属性设置为nonnull。
属性写完后,就需要初始化一个方法,需要在初始化的时候传递一个NSString 的name ,并且最好能给headImage 附上一个默认的头像。
#import <Foundation/Foundation.h>
@class UIImage
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@property (nonatomic, strong, nullable) UIImageView *headImg;
@property (nonatomic, copy, nonnull) NSString *name
- (instancetype)initWithName:(NSString *)name
@end
NS_ASSUME_NONNULL_END
#import "Person.h"
#import <UIKit/UIImage.h>
@implementation Person
- (instancetype)initWithName:(NSString *)name {
if (self = [super init]) {
_name = name;
_headImg = [UIImage imageNamed:@"default_head"]
}
retrun self;
}
@end
看似很简单,似乎没有什么复杂的地方,可是当代码提交之后,协调的开发同事需要使用你的Persion 类创建实例时,却发现并没有默认的头像,这个时候你会感到疑惑,仔细一看同事的代码这样写:
Person *tom = [[Person alloc]init];
tom.name = @"Tom";
同事并没有使用你想让他使用的初始化方法,只使用了最基本的alloc和init, 这是协同开发中常遇到的问题,
如何让同事页使用自己提供的方法呢?
#import <Foundation/Foundation.h>
@class UIImage
NS_ASSUME_NONNULL_BEGIN
@interface Persion : NSObject
@property (nonatomic, strong, nullable) UIImageView *headImg;
@property (nonatomic, copy, nonnull) NSString *name
- (instancetype)initWithName:(NSString *)name;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)new NS_UNAVAILABLE;
@end
NS_ASSUME_NONNULL_END
NS_UNAVAILABLE 修饰在头文件中禁用其他初始化方法,可保证只有自己提供的初始化的方法才是唯一的途径,此时要创建Person 实例,只能通过initWithName 的初始化方法
总结
- 创建类应尽量明确类型,必要时加上前向声明
- 属性当表明可选还是不可选
- 通过NS_UNAVAILABLE 禁用相关的方法。