【问题标题】:How to implement Deep copy for the Class in UIKit?如何在 UIKit 中实现类的深拷贝?
【发布时间】:2012-04-28 14:59:42
【问题描述】:

我有一个对象 imageView1。我想创建另一个对象来保留 imageView1 的深层副本。 像这样,

UIImageView *imageView1 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"background.png"]];
UIImageView *imageView2 = [imageView1 copy];

我知道它不起作用,因为 UIImageView 不符合 NSCopying。但是应该怎么 我愿意吗?

【问题讨论】:

  • 复制 UI 组件在任何架构中都不是一个好主意。
  • @Sulthan 我明白你的意思。但是,我真的需要 UIImageView 来保存一些关于模型的数据。当然,它适用于存储 CGRect、fileName 等。我认为它比直接使用 UIImageView 复杂。感谢您的经验。我会处理好这个。
  • 您应该能够从您的模型创建 UI,而不是让 UI 成为您模型的一部分。

标签: iphone objective-c ios deep-copy


【解决方案1】:

要创建 UIImageView 对象的深层副本,您需要使用 NSKeyedArchiver 将其归档,然后使用 NSKeyedUnarchiver 将其取消归档,但是这种方法存在问题,因为 UIImage 不符合 NSCoding 协议。您首先需要做的是扩展 UIImage 类以支持 NSCoding。

UIImage 上添加一个名为NSCoding 的新类别并放置以下代码:

UIImage+NSCoder.h

#import <UIKit/UIKit.h>

@interface UIImage (NSCoding)
- (id)initWithCoder:(NSCoder *)decoder;
- (void)encodeWithCoder:(NSCoder *)encoder;
@end

UIImage+NSCoder.m

#import "UIImage+NSCoder.h"

@implementation UIImage (NSCoding)
- (id)initWithCoder:(NSCoder *)decoder {
    NSData *pngData = [decoder decodeObjectForKey:@"PNGRepresentation"];
    [self autorelease];
    self = [[UIImage alloc] initWithData:pngData];
    return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
    [encoder encodeObject:UIImagePNGRepresentation(self) forKey:@"PNGRepresentation"];
}

@end

然后在 UIImageView 上添加一个名为 DeepCopy(例如)的新类别,并添加以下代码:

UIImageView+DeepCopy.h

#import <UIKit/UIKit.h>

@interface UIImageView (DeepCopy)
-(UIImageView *)deepCopy;
@end

UIImageView+DeepCopy.m

#import "UIImageView+DeepCopy.h"
#import "UIImage+NSCoder.h"

@implementation UIImageView (DeepCopy)

-(UIImageView *)deepCopy
{
    NSMutableData *data = [[NSMutableData alloc] init];
    NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
    [archiver encodeObject:self forKey:@"imageViewDeepCopy"];
    [archiver finishEncoding];
    [archiver release];

    NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
    UIImageView *imgView = [unarchiver decodeObjectForKey:@"imageViewDeepCopy"];
    [unarchiver release];
    [data release];

    return imgView;
}

@end

用法: 导入你的 UIImageView+DeepCopy.h

UIImageView *imgView1 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"picture.jpg"]];
UIImageView *imageView2 = [imgView1 deepCopy];

【讨论】:

  • 非常聪明!但是,如果我在 imageView1 中有一些子 imageViews。这个方法怎么修改?
  • 这种方法将复制整个对象图,因此您无需更改任何内容。 UIImageView *imgView1 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"picture.jpg"]]; UIImageView *imgView2 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"picture2.jpg"]]; [imgView1 addSubView:imgView2]; UIImageView *copiedResult = [imgView1 deepCopy];
【解决方案2】:

浅拷贝
复制对象的一种方法是浅拷贝。在此过程中,B 与 A 连接到同一内存块。这也称为地址复制。 这会导致 A 和 B 之间共享相同的数据,因此修改一个将改变另一个。现在不再从任何地方引用 B 的原始内存块。如果该语言没有自动垃圾回收,则 B 的原始内存块可能已泄漏。 浅拷贝的优点是它们的执行速度很快,并且不依赖于数据的大小。 不是由整体块组成的对象的按位副本是浅副本。

深拷贝
另一种方法是深拷贝。这里的数据实际上是复制过来的。结果与浅拷贝给出的结果不同。优点是 A 和 B 不相互依赖,但代价是复制速度较慢且成本较高。


因此,遵循 sn-p 将有助于您进行深度复制。

UIImageView *imageView1 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"background.png"]];
UIImageView *imageView2 = [[UIImageView alloc] initWithImage:imageView1.image];

【讨论】:

  • 我试过了。但是,我发现 imageView2.image 是 imageView1.image 的参考。这两个图像具有相同的内存地址。此外,imageView1 可能有一些子视图。
  • @tzzzoz,所以代码不会做深拷贝,对吧?
  • 对于这些 UIImage 对象,我认为它不会做深拷贝。你可以试试看这两个 UIImage 对象的内存地址是否相同。
猜你喜欢
  • 2015-08-23
  • 2016-12-03
  • 1970-01-01
  • 2012-02-19
  • 2015-06-12
  • 2013-03-19
  • 1970-01-01
  • 1970-01-01
  • 2012-04-12
相关资源
最近更新 更多