三 category和enumeration的用法

三 category和enumeration的用法,第1张

概述三 category和enumeration的用法   继承是面向对象程序设计的一个重要特性,但是继承的一些缺点也越来越多被人们意识到。因为继承有时候会破坏类的封装性,使子类可以使用父类的一些非pubic的方法。另外当继承树大到一定程度的时候相信许多程序员都不愿意看到,因为毕竟程序不仅仅是要让计算机运行的,更重要的一点就是要人能够看懂,否则这样的程序也只能束之高阁,供人膜拜了。根据研究表明继承的层 三 category和enumeration的用法

  继承是面向对象程序设计的一个重要特性,但是继承的一些缺点也越来越多被人们意识到。因为继承有时候会破坏类的封装性,使子类可以使用父类的一些非pubic的方法。另外当继承树大到一定程度的时候相信许多程序员都不愿意看到,因为毕竟程序不仅仅是要让计算机运行的,更重要的一点就是要人能够看懂,否则这样的程序也只能束之高阁,供人膜拜了。根据研究表明继承的层次维持3层以下是最容易让人理解。

  所以继承有时候并不表现的那么有用,其实在设计模式中,适配器模式就可以解释用继承是多么的糟糕。那么不用继承objective c如何扩展一个类那,那么Apple的工程师就设计了category这个新语法特性。它的功能就是实现类的扩展而完全不用继承。例如我们要给Nsstring类增加一个新的功能计算Nsstring的长度,并且返回值为NSNumber。假如你要用继承实现

@interface NSNewString:Nsstring

-  (NSNumber)lengthAsNumber;

@end

@implement

- (NSNumber *) lengthAsNumber
{
unsigned int length = [self length];
return ([NSNumber numberWithUnsignedInt: length]);
}

@end

现在我开始调用假如用下面的方法得到一个字符串

NSNewString *str = [Nsstring stringWithFormat:"test"]然后你就会得到编译器的警告,stringWithFormat返回的是Nsstring而你要它成为NSNewString,这种向下转化是极不安全的。所以你写的子类当调用系统API的时候,无法获得你子类的对象结果你的方法就很难用到此处了。

  而下面用category实现就大不一样了。实现如下:首先要给这个category定义一个名字

@interface Nsstring (NumberConvenIEnce)
- (NSNumber *) lengthAsNumber;
@end

@implementation Nsstring (NumberConvenIEnce)
- (NSNumber *) lengthAsNumber
{
unsigned int length = [self length];
return ([NSNumber numberWithUnsignedInt: length]);
} // lengthAsNumber
@end

这样我就是用系统的API获得的Nsstring也可以用我这个方法了。下面就说下category使用的时候应该注意的地方。

  category有2点限制首先你不能在里面定义任何实例变量,它只是方法的扩展,否则你就真的要用继承了。其次category里面的方法不能和原来类中的方法冲突,否则原来类中的方法就无效了。其实cocoa程序设计中同样有方法定义一个类中的方法为失效的。其实cocoa程序设计中代理(delegate)这个概念和category联系时非常紧密的,delegates在cocoa编程后面的讲解中自然就知道是什么了,这里不在赘述了。在appkit中一些控件的使用经常是系统定义了一些category但是不实现它,等到用户使用的时候让用户实现。例如tablevIEw控件中的- (BOol) tableVIEw: (NStableVIEw *) tableVIEw shouldSelectRow: (int) row;方法就是这样实现的。

  下面就来介绍下enumeration的用法,enumeration就是java中常用的迭代器。只不过它比迭代器的语法更简洁。例如NSArray实现了NSEnumeration这个协议就可以用下面的语法迭代集合中的元素。

 

NSArray *array = [NSArray arrayWithObjects:
@"One",@"Two",@"Three",@"Four",nil];
for (Nsstring *element in array)
{
        NSLog(@"element: %@",element);
}
这里你不必指定集合遍历的终止条件,也不用指定下标。这种实现比迭代器用起来方便多了吧,同样NSArray遍历也可以用NSEnumeration来实现。
NSArray *array = [NSArray arrayWithObjects:
@"One",nil];
NSEnumerator *enumerator = [array reverSEObjectEnumerator];
for (Nsstring *element in enumerator)
{
         if ([element isEqualToString:@"Three"])
         {
                break;
         }
}

  任何集合想要实现上面的用法,前提是要实现NSFastEnumeteration这个协议。下面我们就拿一个自己定义的集合例子来实现上面的方法。
#import <Foundation/Foundation.h>
#import <vector>
 
@interface MyFastEnumerationSample : NSObject<NSFastEnumeration>
{
    std::vector<NSInteger> List;
}
 
-(ID)initWithCapacity:(NSUInteger)numItems;
 
@end
 
@implementation MyFastEnumerationSample
 
// 初始化这个List
// 生成一些随机数,插入到List中
// 之后让枚举类返回
-(ID)initWithCapacity:(NSUInteger)numItems
{
    self = [super init];
    if(self != nil)
    {
        for(NSInteger i = 0; i < numItems; ++i)
        {
            List.push_back(random());
        }
    }
    return self;
}
 
// 这个方法就是fastEnumeration协议中的方法,这里你要实现它
// 实现这个方法,你有两种选择
// 1) 使用stackbuf,它是基于array的堆栈。如果用到了这个堆栈,那么你就必需处理下一个参数len的大小。
// 2) 返回你自己的array对象.如果你遍历完了这个array的所有对象,然后就返回0。例如:一个array的链表的实现需要返回每一个array,知道你遍历完所有的array。
// 无论上面那种实现方法,state->itemsPtr 必须要赋予一个合法的array(非空)。这个例子采用了方案1,使用stackbuf来存储结果。
-(NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(ID *)stackbuf count:(NSUInteger)len
{
    NSUInteger count = 0;
    // 下面是初始化,仅仅只会做一次。
    // 确保你从来没有设置state->state为0,或者利用了其他的方法初始化它。
    // (下面是使用state->extra中的一个值)。
    if(state->state == 0)
    {
        // 我们不会跟踪变化,因此我们将设置state->mutationsPtr指向state->extra中的一个数值。
        // 因为这些extra的数值并没有在协议里其他额外的地方使用。
        // If your class was mutable,you may choose to use an internal variable that is updated when the class is mutated.假如你定义的类是可变的,你可能选择使用internal的变量,这个变量可以随着你的类大小的改变而改变。
        // state->mutationsPtr 不能为空。
        state->mutationsPtr = &state->extra[0];
    }
    //现在我们提供items,用来跟踪state->state和决定是否我们已经完成了迭代。
    if(state->state < List.size())
    {
        // 这里需要设置state->itemsPtr指向提供的buffer。
        // 如果需要不停的迭代来实现,那么就可能设置
state->itemsPtr指向一个内部的c数组对象。
        //
state->itemsPtr不能为空。
        state->itemsPtr = stackbuf;
        // 把我们List中提供的所有的items都填充到这个堆栈数组中。
        // 假如我们提供的items太多,这个buffer也仅仅装得下len多的东西。后面的就会丢弃掉
        while((state->state < List.size()) && (count < len))
        {
            // 这个例子在运行中生成内容。
            // 一个真正的实现仅仅会从内部的容器中copy对象。
            stackbuf[count] = [Nsstring stringWithFormat:@"Item %i = %i",state->state,List[state->state]];
            state->state++;
            count++;
        }
    }
    else
    {
        // 我们已经包含了所有的items,因此return 0表明已经做完了。
        count = 0;
    }
    return count;
}
 
@end
 
int main (int argc,const char * argv[])
{
    NSautoreleasePool * pool = [[NSautoreleasePool alloc] init];
 
    // 为了演示,创建一个枚举类的实例,并且枚举里面所有的内容。
    srandomdev();
    MyFastEnumerationSample *example = [[MyFastEnumerationSample alloc] initWithCapacity:50];
    for(ID item in example)
    {
        NSLog(@"%@",item);
    }
 
    [pool drain];
    return 0;
}

好了,这里你的一个fastenumeration就实现了。其实NSNumeration已经实现了NSFastenumeration这个协议。所以假如有一个自己实现的容器,就可以为这个容器定义一个Numeration,它只要继承 NSNumeration,就可以方便的实现 fastenumeration。想想fastNumeration多么方便你就再也不想用什么数组遍历了。

总结

以上是内存溢出为你收集整理的三 category和enumeration的用法全部内容,希望文章能够帮你解决三 category和enumeration的用法所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1068215.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-26
下一篇 2022-05-26

发表评论

登录后才能评论

评论列表(0条)

保存