【问题标题】:UIImage and NSCoding iOS 5.1UIImage 和 NSCoding iOS 5.1
【发布时间】:2012-03-08 07:25:02
【问题描述】:

在 iOS 5.1 之前,如果你想在 UIImage 中使用 NSCoding 协议,你必须这样做。

@interface UIImage (NSCoding)

-(id)initWithCoder:(NSCoder *)deocder;
-(void)encodeWithCoder:(NSCoder *)encoder;

@end

然后自己实现。但是,对于 iOS 5.1,这会在此协议的 .M 文件中生成警告“类别正在实现一个也将由其主类实现的方法”。现在如果我删除这个扩展类 iOS5.1 会很高兴,但是这应该在 iOS4.0 上崩溃和烧毁。那么最好的行动方案是什么?

【问题讨论】:

  • 这个问题已经被问过了,当时是我回答的——我不记得是哪个问题了,因为那是在夏天,但我记得有一个重复的在这里提问。

标签: objective-c ios


【解决方案1】:

我有同样的问题,由于使用子类而不是类别为时已晚,我按照zoul的建议使用class_addMethod,下面是我的实现:

#import "UIImage-NSCoding.h"
#include <objc/runtime.h>
#define kEncodingKey        @"UIImage"

static void __attribute__((constructor)) initialize() {
    @autoreleasepool {

        if (![[UIImage class] conformsToProtocol:@protocol(NSCoding)]) {
            Class class = [UIImage class];

            if (!class_addMethod(
                                 class,
                                 @selector(initWithCoder:), 
                                 class_getMethodImplementation(class, @selector(initWithCoderForArchiver:)),
                                 protocol_getMethodDescription(@protocol(NSCoding), @selector(initWithCoder:), YES, YES).types
                                )) {
                                    NSLog(@"Critical Error - [UIImage initWithCoder:] not defined.");
                                }

            if (!class_addMethod(
                                 class,
                                 @selector(encodeWithCoder:),
                                 class_getMethodImplementation(class, @selector(encodeWithCoderForArchiver:)),
                                 protocol_getMethodDescription(@protocol(NSCoding), @selector(encodeWithCoder:), YES, YES).types
                                )) {
                                    NSLog(@"Critical Error - [UIImage encodeWithCoder:] not defined.");
                                }

        } 
    }
}

@implementation UIImage(NSCoding)

- (id) initWithCoderForArchiver:(NSCoder *)decoder {

    if ((self = [super init]))
    {
        NSData *data = [decoder decodeObjectForKey:kEncodingKey];
        self = [self initWithData:data];
    }

    return self;

}

- (void) encodeWithCoderForArchiver:(NSCoder *)encoder {

    NSData *data = UIImagePNGRepresentation(self);
    [encoder encodeObject:data forKey:kEncodingKey];

}

@end

到目前为止,我还没有注意到任何进一步的问题。希望它有所帮助!

[注意]

如果您使用此 UIImage 类别来归档 UIImageViewer 对象,请注意 iOS 5 中 UIImageViewer 的 NSCoding 实现似乎已损坏。 UIImageViewer 的图像属性在保存和加载后丢失,当它在 XIB 中指定时(我没有尝试查看在代码中创建 UIImageViewer 对象时是否存在相同的问题)。这已在 iOS 6 中修复。

[更新]

我将代码更改为在 +load 中添加方法而不是 initialize(),它仍然只执行一次,但更早。我目前的实现:

#import "UIImage+NSCoding.h"
#import <objc/runtime.h>
#define kEncodingKey        @"UIImage"

@implementation UIImage (NSCoding)

+ (void) load
{

    @autoreleasepool {
        if (![UIImage conformsToProtocol:@protocol(NSCoding)]) {
            Class class = [UIImage class];
            if (!class_addMethod(
                                 class,
                                 @selector(initWithCoder:), 
                                 class_getMethodImplementation(class, @selector(initWithCoderForArchiver:)),
                                 protocol_getMethodDescription(@protocol(NSCoding), @selector(initWithCoder:), YES, YES).types
                                 )) {
                NSLog(@"Critical Error - [UIImage initWithCoder:] not defined.");
            }

            if (!class_addMethod(
                                 class,
                                 @selector(encodeWithCoder:),
                                 class_getMethodImplementation(class, @selector(encodeWithCoderForArchiver:)),
                                 protocol_getMethodDescription(@protocol(NSCoding), @selector(encodeWithCoder:), YES, YES).types
                                 )) {
                NSLog(@"Critical Error - [UIImage encodeWithCoder:] not defined.");
            }

        } 
    }
}

- (id) initWithCoderForArchiver:(NSCoder *)decoder {
    if (self = [super init]) {
        NSData *data = [decoder decodeObjectForKey:kEncodingKey];
        self = [self initWithData:data];
    }

    return self;

}

- (void) encodeWithCoderForArchiver:(NSCoder *)encoder {

    NSData *data = UIImagePNGRepresentation(self);
    [encoder encodeObject:data forKey:kEncodingKey];

}

@end

【讨论】:

  • 您介意提供一些有关此代码将去哪里的上下文吗?我有一个包含 2 个 NSString 和一个 UIImage 的自定义对象类,它会放在那里吗?
  • 以上代码实现为UIImage的类别。基本上我所做的只是扩展 Jeff LaMarche 已经实现的 (iphonedevelopment.blogspot.it/2009/03/uiimage-and-nscoding.html) 以避免在 iOS 5.1 中出现警告消息。
  • [[UIImage class] conformsToProtocol:@protocol(NSCoding)]可以写成[UIImage conformsToProtocol:@protocol(NSCoding)]
  • 这会正确保存和加载图像的scale 属性吗?
  • 你在initWithCoderForArchiver方法中对同一个对象调用了两次init
【解决方案2】:

与其在 UIImage 上使用类别,不如将其子类化不是更简洁吗?

然后你可以实现 initWithCoderencodeWithCoder 并在 5.1 和 pre-5.1 上使用 UIImage 的 NSCoding 实现。

【讨论】:

  • 我只需要一个地方保存 UIImage,子类化将要求我始终使用我发明的这个新的 Image 类。
【解决方案3】:

一种方法是在 iOS 4 上运行时动态修补 UIImage 类以添加所需的方法(或者更好的是,当方法缺失时)。 Here’s a sample project on GitHub 做了类似的事情,它增加了对 iOS 4 的控制器包含支持(关键是 class_addMethod 函数)。但这对于生产代码来说太神奇了,所以如果有更简单的答案,那就去吧。

【讨论】:

  • 使用运行时处理类是脆弱的,很难预见可能出现的问题。
猜你喜欢
  • 2012-12-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多