UIImage и NSCoding для iOS 5.1

До iOS 5.1, если вы хотели использовать протоколы NSCoding с UIImage, вы должны были сделать что-то вроде этого.

@interface UIImage (NSCoding)

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

@end

Тогда реализуй это сам. Однако с iOS 5.1 это генерирует предупреждение "Категория реализует метод, который также будет реализован его основным классом" в файле.M для этого протокола. Теперь, если я удалю это расширение, класс iOS5.1 будет счастлив, однако это должно вылететь и сгореть на iOS4.0. Итак, каков наилучший курс действий?

3 ответа

Решение

У меня та же проблема, и поскольку уже слишком поздно использовать подкласс, а не категрию, я последовал предложению 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, имейте в виду, что реализация UIImageViewer в iOS 5 в NSCoding кажется сломанной. Свойство image 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

Вместо того, чтобы использовать категорию в UIImage, не будет ли чище подкласс ее?

Тогда вы могли бы реализовать initWithCoder а также encodeWithCoder и использовать реализацию NSCoding UIImage на 5.1 и свою собственную на pre-5.1.

Одним из способов является динамическое исправление UIImage Класс для добавления необходимых методов при работе на iOS 4 (или лучше, когда методы отсутствуют). Вот пример проекта на GitHub, который делает нечто подобное, добавляет поддержку сдерживания контроллера для iOS 4 (ключ class_addMethod функция). Но это слишком много волшебства для производственного кода, поэтому, если есть более простой ответ, соглашайтесь.

Другие вопросы по тегам