上一章Objective-C开发教程请查看:Objective-C多态性和继承
Objective-C支持面向对象编程(OOP),对类的设计尤为重要,首先就是数据封装了,数据封装主要是对外隐藏实现细节,按照惯例:
- 属性一般设为私有,外部若需要访问,可以设计对应的方法进行间接访问,或者直接使用@property声明属性即可。
- 对外提供功能的方法设为公有,提供内部使用的为私有,但是OC中没有私有方法,不过可以通过extension扩展实现,这在后面的章节会讨论到。
但是要注意,一般来说我们在实现算法的时候是会用到私有方法的,不能使用和公有方法相同的形式,既然OC是C的超集,那么我可以使用C的私有函数来实现OC的私有方法,C中使用static修饰的方法都是文件内部方法,只能在当前文件中可用,而且需要在调用它的函数之前声明,如下:
static void print(NSString *str){
NSLog(@"print: %@", str);
}
-(instancetype)init{
if(self = [super init]){
print(@"init ANLog......");
}
return self;
}
类的设计
接着我想说下关于类的设计,以前刚刚开始学Java的时候,老是看到OOP强调的继承、封装和多态,实际上,你压根不用担心不会用到这些特性,只要你用的是OOP语言,自然书写类的时候已经是在进行标准的数据封装了,最好注意一下不需暴露的属性和方法设为私有即可。
这里我想说的是,类的设计难点不是要符合OOP编程规范,而是一般可能忽略掉数据结构和算法了。
对于一个类A,其中包含数据属性和方法,创建不同的A类对象的时候,其中的数据属性对于每个对象都占有一份内存空间,方法一般都是只有一份。
最简单的数据结构就是基本数据类型,如int、float、double等,这些数据结构已经隐含了可用的算法了:加减乘除等。我们对类的设计也是设计一种数据类型,那么也需要包含操作数据的算法。而又因为类的数据可能是复合类型的:也就是包含多个属性,那么我们可以得出:
- 核心操作的属性数据成员,或者需要全局使用的属性,应该整体另外使用一个类封装——或者使用泛型NSObject或id代替。
- 如有需要有关于数据结构的类封装,这是一个简单类,用于说明数据的基本结构。数据结构一般包含实际或泛型表示的数据。
- 核心操作算法包含数据结构,并且可能有对应的数据成员,但是这里的成员一般与算法相关。
假设我们需要实现一个任务队列,用于顺序处理用户的请求,那么这里的核心数据就是用户的任务了,数据结构就是队列或优先队列,接着基本操作算法包括:
- push,添加任务。
- pop,取出任务。
- process,处理任务。
对于实际的任务,我们可以使用一个block代码块来表示,或者使用函数指针也是没问题的。
数据封装实例
首先第一步是设计任务,然后是数据结点,最后是算法封装类,声明代码如下:
#import <Foundation/Foundation.h>
typedef void (^ANBlock)();
// 数据封装类
@interface ANTask : NSObject
@property(nonatomic, assign)long tid;
@property(nonatomic, copy)NSString *tname;
@property(nonatomic, strong)ANBlock task;
@end
// 结点封装类
@interface ANTaskNode : NSObject
@property(nonatomic)ANTask *task;
@property(nonatomic)ANTaskNode *prev;
@property(nonatomic)ANTaskNode *next;
@end
// 算法所在类
@interface ANTaskQueue : NSObject
@property(nonatomic)ANTaskNode *head;
@property(nonatomic)ANTaskNode *tail;
@property(nonatomic, assign)int size;
-(BOOL)isEmpty;
-(void)pushTask: (ANBlock)task withId: (long)tid andName: (NSString*)tname;
-(ANTask*)popTask;
-(void)processTask: (ANTask*)task;
@end
以上设计和声明并不算复杂,喜欢就好,千万不要为简单起见将所有东西都涉及在算法操作类中,否则将会非常之混乱,即使自己看得明白,别人看上去也不想看下去了。
接着就是代码实现的部分了,如果还没有数据结构和算法的继承的,可以参考本网站中的对应内容,其中相当详细地介绍了数据结构和算法的原理和实现。
//
// ANTaskQueue.m
// basic
//
// Created by cococ on 2020/1/4.
// Copyright © 2020年 cococ. All rights reserved.
//
#import "ANTaskQueue.h"
@implementation ANTask
-(instancetype)init{
if(self = [super init]){
_tid = -1;
_tname = nil;
_task = nil;
}
return self;
}
@end
@implementation ANTaskNode
-(instancetype)init{
if(self = [super init]){
_prev = _next = nil;
_task = [[ANTask alloc]init];
}
return self;
}
@end
@implementation ANTaskQueue
-(instancetype)init{
if(self = [super init]){
_head = _tail = nil;
_size = 0;
}
return self;
}
-(BOOL)isEmpty{
return _size == 0;
}
static ANTaskNode* taskNode(ANBlock task, long tid, NSString *tname){
ANTaskNode *node = [[ANTaskNode alloc]init];
node.task.tid = tid;
node.task.tname = tname;
node.task.task = task;
return node;
}
-(void)pushTask: (ANBlock)task withId: (long)tid andName: (NSString*)tname{
if(task == nil || tname == nil){
NSLog(@"task or tname is nil");
return;
}
ANTaskNode *node = taskNode(task, tid, tname);
if(_size == 0){
node.prev = node.next = node;
_head = _tail = node;
_size++;
}
else{
node.prev = _tail;
node.next = _head;
_tail.next = node;
_head.prev = node;
_tail = node;
_size++;
}
}
-(ANTask*)popTask{
if(_size == 0)
return nil;
ANTaskNode *head = _head;
ANTask *task = head.task;
head.prev.next = head.next;
head.next.prev = head.prev;
_head = head.next;
head = nil;
_size--;
return task;
}
-(void)processTask: (ANTask*)task{
NSLog(@"id: %ld", task.tid);
NSLog(@"name: %@", task.tname);
task.task();
NSLog(@"");
}
@end
最后就是测试用例了,先添加任务,然后使用while循环逐个拿出来进行处理:
void testTaskQueue(void){
ANTaskQueue *taskQueue = [[ANTaskQueue alloc]init];
[taskQueue pushTask:^(){
NSLog(@"Task A: Get Message From Server.......");
} withId:54656 andName:@"Server Request"];
[taskQueue pushTask:^(){
NSLog(@"Task B: Post Info And Login.......");
} withId:3249 andName:@"User Login"];
[taskQueue pushTask:^(){
NSLog(@"Task C: Enter User Info Detail.......");
} withId:8418 andName:@"Visit Info"];
[taskQueue pushTask:^(){
NSLog(@"Task D: Timer Task.......");
} withId:78499 andName:@"Timer"];
while (![taskQueue isEmpty]) {
[taskQueue processTask:[taskQueue popTask]];
}
}
int main(int argc, const char * argv[]) {
testTaskQueue();
return 0;
}
评论前必须登录!
注册