个性化阅读
专注于IT技术分析

Objective-C类设计和数据封装 – Objective-C开发教程

上一章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;
}
赞(0)
未经允许不得转载:srcmini » Objective-C类设计和数据封装 – Objective-C开发教程

评论 抢沙发

评论前必须登录!