IOS 之 NSUndoManager撤销与恢复系列

     IOS提供了撤销与恢复操作的类NSUndoManager,使用比较简单.

     NSUndoManager是UIResponder的成员变量所以说只要是UIResponder的子类都拥有NSUndoManager. 

     再来就是介绍一下NSInvocation类的作用. 在 iOS中可以直接调用 某个对象的消息 方式有2种.

     一种是performSelector:withObject: 方式比较简单, 能完成简单的调用.

     再一种就是NSInvocation,我们可以使用NSInvocation来把一些对象的消息封装成新的对象, NSInvocation可以处理参数、返回值,使用方法如下:

//方法签名类,需要被调用消息所属的类AsynInvoke ,被调用的消息invokeMethod:
NSMethodSignature *sig= [[AsynInvoke class] instanceMethodSignatureForSelector:@selector(invokeMethod:)];
//根据方法签名创建一个NSInvocation
NSInvocation *invocation=[NSInvocation invocationWithMethodSignature:sig];
//设置调用者也就是AsynInvoked的实例对象,在这里我用self替代
[invocation setTarget:self];
//设置被调用的消息
[invocation setSelector:@selector(invokeMethod:)];
//如果此消息有参数需要传入,那么就需要按照如下方法进行参数设置,需要注意的是,atIndex的下标必须从2开始。原因为:0 1 两个参数已经被target和selector占用
NSInteger num=10;
[invocation setArgument:&num atIndex:2];
//retain 所有参数,防止参数被释放dealloc
[invocation retainArguments];
//消息调用
[invocation invoke];
//如果调用的消息有返回值,那么可进行以下处理   获得返回值类型
const char *returnType = sig.methodReturnType;
//声明返回值变量
id returnValue;
//如果没有返回值,也就是消息声明为void,那么returnValue=nil
if( !strcmp(returnType, @encode(void)) ){
  returnValue =  nil;
}
//如果返回值为对象,那么为变量赋值
else if( !strcmp(returnType, @encode(id)) ){
  [invocation getReturnValue:&returnValue];
}else{
  //如果返回值为普通类型NSInteger  BOOL
  //返回值长度
  NSUInteger length = [sig methodReturnLength];
  //根据长度申请内存
  void *buffer = (void *)malloc(length);
  //为变量赋值
  [invocation getReturnValue:buffer];
  if( !strcmp(returnType, @encode(BOOL)) ) {
    returnValue = [NSNumber numberWithBool:*((BOOL*)buffer)];
  }else if( !strcmp(returnType, @encode(NSInteger)) ){
    returnValue = [NSNumber numberWithInteger:*((NSInteger*)buffer)];
  }
  returnValue = [NSValue valueWithBytes:buffer objCType:returnType];
}

以下是整体的操作流程.

//操作
-(void)setText
{
    NSString* temp = [NSString stringWithFormat:@"更改"];
    self.label.text = temp;
}
//反操作
-(void)unsetText
{
    NSString* temp = [NSString stringWithFormat:@"还原"];
    self.label.text = temp;
}
//3个按钮事件
- (IBAction)ButtonHit:(id)sender {
    UIButton* bt = (UIButton*)sender;
    //封装操作方法
    NSMethodSignature* method = [self methodSignatureForSelector:@selector(setText)];
    NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:method];
    [invocation setTarget:self];
    [invocation setSelector:@selector(setText)];
    //封装反操作方法
    NSMethodSignature* method1 = [self methodSignatureForSelector:@selector(unsetText)];
    NSInvocation* invocation1 = [NSInvocation invocationWithMethodSignature:method1];
    [invocation1 setTarget:self];
    [invocation1 setSelector:@selector(unsetText)];
    
    switch ( bt.tag ) {
        case 1:
            [self aundo:invocation un:invocation1];
            break;
        case 4:
            [self.undoManager undo]; //撤销    这里会执行 undo:un:方法
            break;
        case 5:
            [self.undoManager redo]; //恢复    这里会执行 aredo:un:方法
            break;
        default:
            break;
    }
}
//执行操作并把反操作加入到撤销栈里
-(void)aundo:(NSInvocation*)invocation un:(NSInvocation*)uninvocation
{
    [[self.undoManager prepareWithInvocationTarget:self] aredo:uninvocation un:invocation];
    [invocation invoke];
}
//执行反操作,并把操作添加到恢复栈
-(void)aredo:(NSInvocation*)invocation un:(NSInvocation*)uninvocation
{
    [[self.undoManager prepareWithInvocationTarget:self] aundo:uninvocation un:invocation];
    [invocation invoke];
}

NSUndoManager工作原理就是把操作方法和撤销封装成NSInvocation对象,存放在内部的堆栈里,这样每操作一次就会push一次到撤销栈,每撤销一次就会pop出撤销栈,然后再push到恢复栈.

你可能感兴趣的:(IOS开发技术)