Ошибка кодирования NSAttributedString

На основании принятого ответа наэтот вопрос Я написал следующий код:

<code>NSData* somedata;
somedata=[NSKeyedArchiver archivedDataWithRootObject:ts];
</code>

где ts - это NSAttributedString, которая заполняется некоторым текстом и некоторыми атрибутами (в данном случае цветами).

Когда я выполняю этот код, я получаю эту ошибку:

<code>*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFType encodeWithCoder:]: unrecognized selector sent to instance 0x6eb5b90'
</code>

Я новичок в области NSCoder, но ответ на вышеупомянутый вопрос заставил меня подумать, что это все, что мне нужно сделать. Это? Я что-то пропустил?

РЕДАКТИРОВАТЬ:

Нераспознанный селектор в этом случае отправляется атрибуту цвета в NSAttributedString. Когда я инициализирую строку примерно так:

<code>NSAttributedString *ts = [[NSAttributedString alloc] initWithString:text attributes:self.currentAttributeDictionary];
</code>

Словарь построен так:

<code>self.currentAttributeDictionary=[NSDictionary dictionaryWithObjectsAndKeys:
                                 [self.currentColor CGColor],(NSString*)kCTForegroundColorAttributeName,
                                 nil];
</code>

И NSLog словаря дает это:

<code>New dictionary is: {
CTForegroundColor = "<CGColor 0x6eb5b90> [<CGColorSpace 0x6e968c0> (kCGColorSpaceDeviceRGB)] ( 1 1 0 1 )";}
</code>

Приведенный выше адрес CGColor совпадает с адресом в сообщении об ошибке.

 Conrad Shultz11 мая 2012 г., 23:09
__NSCFType менее чем полезен. Каков фактический объект в 0x68818a0? Звучит так (предполагается, что * ts - это то, что вы думаете), что-то связано с NSAttributedString, которое не соответствует NSCoding.
 Chris11 мая 2012 г., 23:30
Ага. Это было самое НЕПРАВИЛЬНОЕ. Вопрос был обновлен. Благодарю.

Ответы на вопрос(2)

Решение Вопроса

В то время какUIColor соответствуетNSCoding(в отличие от большинства таких классов)not бесплатный номер соединен сCGColorRef, Ваш словарь пытается закодировать его содержимое, иCGColorRef не знает, как себя кодировать.

Предполагая, что вы не хотите кодироватьUIColor вместо этого (поскольку они звучат как атрибуты Core Text), вам придется обрабатывать сериализациюCGColorRef сам. Смотрите, например,этот вопрос за некоторые полезные мысли.

Следует отметить, между прочим, поскольку я не знаю, куда направляются архивные данные, что если вы хотите разархивировать данные в OS X, эти цвета снова становятся головной болью на уровне AppKit / UIKit:NSColor а такжеUIColor напрямую не совместимы, так что вам все равно нужно пройти черезCGColorRefсохраняя информацию о цветовом пространстве соответствующим образом.

 Chris12 мая 2012 г., 00:42
Отлично. Еще раз спасибо.
 Chris17 мая 2012 г., 20:30
Ради полноты я так и сделал. Написал свои собственные биты, чтобы закодировать словарь самостоятельно. Работал как чемпион. Благодарю.
 Chris12 мая 2012 г., 00:39
Хм. Так что, возможно, подкласс CGColor и реализовать encodeWithCoder? Благодарю.
 12 мая 2012 г., 00:40
CGColorRef является непрозрачным CFType - на самом деле он не является объектом, поэтому вы не можете его наследовать. Возможно, вам понадобится добавить некоторый код переноса вне словаря, который принимает словарь и кодирует его, особенно обрабатывая значения CGColorRef.
 20 июн. 2013 г., 21:04
@ Крис, не могли бы вы поделиться своим кодом? поможет безмерно.

По запросу, вот код, который я использовал, чтобы выполнить то, что мне нужно было сделать. Прошел год с тех пор, как я посмотрел этот код, и он был написан больше для того, чтобы понять, что происходит, чем для хороших практик кодирования или для какой-либо эффективности. Тем не менее, он работал, и он работал отлично!

Я определил категорию кода NSAttributedString ниже.

Пример использования:

-(void)code:(id)sender {    
    self.testData=[textView.attributedString customEncode];
    NSLog(@"%@",self.testData);
}

-(void)recover:(id)sender {
    NSAttributedString* tString=[NSMutableAttributedString customDecode:self.testData];
    NSLog(@"Recover pressed: %@",tString);
    textView.attributedString=tString;
}

И вот основной код:

#import "NSAttributedString+Extras.h"
#import <CoreText/CoreText.h>

@implementation NSAttributedString (Extras)

-(NSData*)customEncode {
    __block NSMutableArray* archivableAttributes=[[NSMutableArray alloc]init];

    [self enumerateAttributesInRange:NSMakeRange(0, [self length]) options:0 usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) {
        NSLog(@"range: %d %d",range.location, range.length);
        NSLog(@"dict: %@",attrs);
        NSLog(@"keys: %@", [attrs allKeys]);
        NSLog(@"values: %@", [attrs allValues]);

        NSMutableDictionary* tDict=[[NSMutableDictionary alloc]init];

        [tDict setObject:[NSNumber numberWithInt:range.location] forKey:@"location"];
        [tDict setObject:[NSNumber numberWithInt:range.length] forKey:@"length"];

        for (NSString* tKey in [attrs allKeys]) {
            if ([tKey isEqualToString:@"CTUnderlineColor"]) {
                [tDict setObject:[NSAttributedString arrayFromCGColorComponents:((CGColorRef)[attrs objectForKey:@"CTUnderlineColor"])] forKey:@"CTUnderlineColor"];
            }
            if ([tKey isEqualToString:@"NSUnderline"]) {
                NSNumber* underline=[attrs objectForKey:@"NSUnderline"];
                [tDict setObject:underline forKey:@"NSUnderline"];
            }
            if ([tKey isEqualToString:@"CTForegroundColor"]) {
                [tDict setObject:[NSAttributedString arrayFromCGColorComponents:((CGColorRef)[attrs objectForKey:@"CTForegroundColor"])] forKey:@"CTForegroundColor"];
            }
            if ([tKey isEqualToString:@"NSFont"]) {
                CTFontRef font=((CTFontRef)[attrs objectForKey:@"NSFont"]);

                NSDictionary* fontDict=[NSDictionary 
                                        dictionaryWithObjects:
                                        [NSArray arrayWithObjects:(NSString*)CTFontCopyPostScriptName(font),[NSNumber numberWithFloat:CTFontGetSize(font)], nil]
                                        forKeys:
                                        [NSArray arrayWithObjects:@"fontName", @"fontSize", nil]];

                [tDict setObject:fontDict forKey:@"NSFont"];
            }
        }

        [archivableAttributes addObject:tDict];
    }];

    NSMutableDictionary* archiveNSMString=[NSMutableDictionary 
                                           dictionaryWithObjects: [NSArray arrayWithObjects:[self string],archivableAttributes,nil]
                                           forKeys:[NSArray arrayWithObjects:@"string",@"attributes",nil]];

    NSLog(@"archivableAttributes array: %@",archiveNSMString);

    NSData* tData=[NSKeyedArchiver archivedDataWithRootObject:archiveNSMString];

    NSLog(@"tdata: %@",tData);

    return tData;
}

+(NSAttributedString*)customDecode:(NSData *)data {
    NSMutableAttributedString* tString;
    NSMutableDictionary* tDict=[NSKeyedUnarchiver unarchiveObjectWithData:data];
    NSArray* attrs;

    CTFontRef font=NULL;
    CGColorRef color=NULL;
    NSNumber* underlineProp=[NSNumber numberWithInt:0];
    CGColorRef underlineColor=NULL;

    NSLog(@"decoded dictionary: %@",tDict);

    if ([[tDict allKeys]containsObject:@"string"]) {
        tString=[[NSMutableAttributedString alloc]initWithString:((NSString*)[tDict objectForKey:@"string"])];
    }
    else {
        tString=[[NSMutableAttributedString alloc]initWithString:@""];
    }

    if ([[tDict allKeys]containsObject:@"attributes"]) {
        attrs=[tDict objectForKey:@"attributes"];
    }
    else {
        attrs=nil;
    }

    for (NSDictionary* attDict in attrs) {
        int location=-1;
        int length=-1;
        NSRange insertRange=NSMakeRange(-1, 0);

        if ([[attDict allKeys]containsObject:@"location"]) {
            location=[[attDict objectForKey:@"location"]intValue];
        }
        if ([[attDict allKeys]containsObject:@"length"]) {
            length=[[attDict objectForKey:@"length"]intValue];
        }
        if (location!=-1&&length!=-1) {
            insertRange=NSMakeRange(location, length);
        }

        if ([[attDict allKeys]containsObject:@"NSUnderline"]) {
            underlineProp=[attDict objectForKey:@"NSUnderline"];
        }

        if ([[attDict allKeys]containsObject:@"CTUnderlineColor"]) {
            underlineColor=[NSAttributedString cgColorRefFromArray:[attDict objectForKey:@"CTUnderlineColor"]];
        }        

        if ([[attDict allKeys]containsObject:@"CTForegroundColor"]) {
            color=[NSAttributedString cgColorRefFromArray:[attDict objectForKey:@"CTForegroundColor"]];
        }

        if ([[attDict allKeys]containsObject:@"NSFont"]) {
            NSString* name=nil;
            float size=-1;

            NSDictionary* fontDict=[attDict objectForKey:@"NSFont"];

            if ([[fontDict allKeys]containsObject:@"fontName"]) {
                name=[fontDict objectForKey:@"fontName"];
            }
            if ([[fontDict allKeys]containsObject:@"fontSize"]) {
                size=[[fontDict objectForKey:@"fontSize"]floatValue];
            }

            if (name!=nil&&size!=-1) {
                font=CTFontCreateWithName((CFStringRef)name, size, NULL);
            }
        }

        if (insertRange.location!=-1) {
            if (color!=NULL) {
                [tString addAttribute:(NSString*)kCTForegroundColorAttributeName value:(id)color range:insertRange];
            }
            if (font!=NULL) {
                [tString addAttribute:(NSString*)kCTFontAttributeName value:(id)font range:insertRange];
            }
            if ([underlineProp intValue]!=0&&underlineColor!=NULL) {
                [tString addAttribute:(NSString*)kCTUnderlineColorAttributeName value:(id)underlineColor range:insertRange];
                [tString addAttribute:(NSString*)kCTUnderlineStyleAttributeName value:(id)underlineProp range:insertRange];
            }
       }
    } 

    [tString enumerateAttributesInRange:NSMakeRange(0, [tString length]) options:0 usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) {
        NSLog(@"range: %d %d",range.location, range.length);
        NSLog(@"dict: %@",attrs);
        NSLog(@"keys: %@", [attrs allKeys]);
        NSLog(@"values: %@", [attrs allValues]);
    }];

    return [[NSAttributedString alloc]initWithAttributedString:tString];
}

+(NSArray*)arrayFromCGColorComponents:(CGColorRef)color {
    int numComponents=CGColorGetNumberOfComponents(color);
    CGFloat* components=CGColorGetComponents(color);
    NSMutableArray* retval=[[NSMutableArray alloc]init];
    for(int i=0;i<numComponents;i++) {
        [retval addObject:[NSNumber numberWithFloat:components[i]]];
    }
    return [NSArray arrayWithArray:retval];
}

+(CGColorRef)cgColorRefFromArray:(NSArray*)theArray {
    CGFloat* array=malloc(sizeof(CGFloat)*[theArray count]);
    for (int i=0; i<[theArray count]; i++) {
        array[i]=[[theArray objectAtIndex:i]floatValue];
    }

    CGColorSpaceRef theSpace;

    if ([theArray count]==2) {
        theSpace=CGColorSpaceCreateDeviceGray();
    }
    else {
        theSpace=CGColorSpaceCreateDeviceRGB();
    }

    return CGColorCreate(theSpace, array);
}

@end

Ваш ответ на вопрос