Вопрос по objective-c, nscoder, nskeyedarchiver, nsattributedstring – Ошибка кодирования NSAttributedString

0

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

<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 совпадает с адресом в сообщении об ошибке.

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

Ваш Ответ

2   ответа
6

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

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

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

Ради полноты я так и сделал. Написал свои собственные биты, чтобы закодировать словарь самостоятельно. Работал как чемпион. Благодарю. Chris
Хм. Так что, возможно, подкласс CGColor и реализовать encodeWithCoder? Благодарю. Chris
Отлично. Еще раз спасибо. Chris
CGColorRef является непрозрачным CFType - на самом деле он не является объектом, поэтому вы не можете его наследовать. Возможно, вам понадобится добавить некоторый код переноса вне словаря, который принимает словарь и кодирует его, особенно обрабатывая значения CGColorRef.
@ Крис, не могли бы вы поделиться своим кодом? поможет безмерно.
0

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

Я определил категорию кода 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

Похожие вопросы