gnustep-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Fwd: Bug: libobjc2 property name introspection.


From: Maxthon Chan
Subject: Fwd: Bug: libobjc2 property name introspection.
Date: Wed, 10 Apr 2013 00:19:56 +0800

Sorry I messed up a little.

下面是被转发的邮件:

发件人: Maxthon Chan <address@hidden>
主题: 回复: Bug: libobjc2 property name introspection.
日期: 2013年4月10日 GMT+0800上午12时19分14秒
收件人: David Chisnall <address@hidden>

I will just copy/paste some code from my actual project. Compile without -DGNUSTEP:

WFDecls.h:

//
//  WFDecls.h
//  FusionKit.D
//
//  Created by Maxthon Chan on 13-4-8.
//  Copyright (c) 2013 myWorld Creations. All rights reserved.
//

#ifndef FusionKit_D_WFDecls_h
#define FusionKit_D_WFDecls_h

#ifndef __OBJC__
#error This library will build only with Objective-C.
#endif

#if !__has_feature(blocks)
#error This library require blocks to build.
#endif

#if !__has_feature(objc_arc)
#error This library require Objective-C ARC to build.
#endif

#import <Foundation/Foundation.h>

#if defined(__cplusplus)
#define WFBeginDecls extern "C" {
#define WFEndDecls }
#define WFExtern extern "C"
#else
#define WFBeginDecls
#define WFEndDecls
#define WFExtern extern
#endif // defined(__cplusplus)

WFBeginDecls

#if defined(DEBUG)
#define WFLog(format, ...) NSLog(format, ##__VA_ARGS__)
#else
#define WFLog(format, ...)
#endif

#define WFStaticString(name, value) WFExtern NSString *const name
#define WFStaticStringValue(name, value) NSString *const name = value

#define WFSTR(format, ...) ([NSString stringWithFormat:format, ##__VA_ARGS__])
#define WFType(type) (@(@encode(type)))
#define WFThisBundle ([NSBundle bundleForClass:[self class]])
#define WFClassFromSelector(selector, dictionary) (NSClassFromString((dictionary)[NSStringFromSelector(selector)]))

#define WFAssignPointer(pointer, value) do { \
typeof(pointer) __ptr = pointer; \
if (__ptr) *__ptr = value; \
} while(0)

#if !defined(NS_INLINE)
#if defined(__GNUC__)
#define NS_INLINE static __inline__ __attribute__((always_inline))
#elif defined(__MWERKS__) || defined(__cplusplus)
#define NS_INLINE static inline
#elif defined(_MSC_VER)
#define NS_INLINE static __inline
#elif TARGET_OS_WIN32
#define NS_INLINE static __inline__
#endif
#endif

#if defined(GNUSTEP)
#define WFEndProperties @property id _end
#else
#define WFEndProperties
#endif

WFEndDecls

#endif // FusionKit_D_WFDecls_h

WFObject.h

//
//  WFObject.h
//  FusionKit.D
//
//  Created by Maxthon Chan on 13-4-8.
//  Copyright (c) 2013 myWorld Creations. All rights reserved.
//

#import <FusionKit/WFDecls.h>

WFBeginDecls

@class WFObject;

#define WFFrameworkBundle() [NSBundle bundleForClass:[WFObject class]]

@interface WFObject : NSObject

- (id)initWithDictionary:(NSDictionary *)dictionary;

- (NSDictionary *)dictionaryRepresentation;
- (Class)classForProperty:(NSString *)property;

@end

@interface WFObject (WFObjectJSON)

- (id)initWithJSONData:(NSData *)data error:(NSError **)error;

- (BOOL)canRepresentInJSON;
- (NSData *)JSONDataWithError:(NSError **)error;

@end

@interface WFObject (WFEquality)

- (id)ID;
- (BOOL)isEqual:(id)object;

@end

WFEndDecls

WFObject.m

//
//  WFObject.m
//  FusionKit.D
//
//  Created by Maxthon Chan on 13-4-8.
//  Copyright (c) 2013 myWorld Creations. All rights reserved.
//

#import "WFObject.h"
#import <objc/runtime.h>
#import <objc/message.h>
#import "WFConstants.h"

@implementation WFObject

- (id)initWithDictionary:(NSDictionary *)dictionary
{
    if (self = [super init])
    {
        // Get a list of properties.
        unsigned int propertyCount = 0;
        objc_property_t *properties = class_copyPropertyList([self class], &propertyCount);
        
        if (properties)
        {
            // Enumerate all properties.
            
            for (unsigned int i = 0; i < propertyCount; i++)
            {
                objc_property_t property = properties[i];
                NSString *name = @(property_getName(property)); // Property name.
                NSString *attr = @(property_getAttributes(property)); // Property attributes
                NSString *type = @"@";
                BOOL readonly = NO;
                // WFLog(@"Accessing property %@ with attributes address@hidden", name, attr);
                
                // Check the property type.
                NSArray *attrs = [attr componentsSeparatedByString:@","];
                for (NSString *attribute in attrs)
                {
                    if ([attribute hasPrefix:@"R"])
                    {
                        readonly = YES;
                    }
                }
                
                id value = dictionary[name]; // Find the value.
                
                if (!value && [name isEqualToString:@"ID"])
                {
                    value = dictionary[@"id"]; // ID is used instead of id.
                }
                
                //if (!value)
                //    NSLog(@"WARNING: Cannot determine value for property %@, nil is used.", name);
                
                if (!readonly && value)
                {
                    // Dispatching would require some tricks.
                    if ([type hasPrefix:WFType(id)]) // Objects. Special requirements is required.
                    {
                        Class class = [self classForProperty:name];
                        id object = value;
                        
                        //if (!class)
                        //    NSLog(@"WARNING: Class for property %@ cannot be determined.", name);
                        
                        if (class && [class isSubclassOfClass:[WFObject class]])
                        {
                            if ([value isKindOfClass:[NSDictionary class]])
                                object = [[class alloc] initWithDictionary:value];
                            else if ([value isKindOfClass:[NSArray class]])
                            {
                                NSArray *array = value;
                                NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[array count]];
                                for (id item in array)
                                {
                                    if ([item isKindOfClass:[NSDictionary class]])
                                    {
                                        [mutableArray addObject:[[class alloc] initWithDictionary:item]];
                                    }
                                    else
                                    {
                                        //NSLog(@"WARNING: Object typed %@ occured in array asking for objects typed address@hidden", NSStringFromClass([item class]), NSStringFromClass(class));
                                        [mutableArray addObject:item];
                                    }
                                }
                                object = [mutableArray copy];
                            }
                        }
                        [self setValue:object forKey:name];
                    }
                    else
                    {
                        // Everything else.
                        [self setValue:value forKey:name];
                    }
                }
            }
            free(properties);
            properties = NULL;
        }
    }
    return self;
}

- (Class)classForProperty:(NSString *)property
{
    objc_property_t objcProperty = class_getProperty([self class], [property cStringUsingEncoding:[NSString defaultCStringEncoding]]);
    NSString *attributes = @(property_getAttributes(objcProperty));
    NSString *type = @"@";
    Class class = Nil;
    
    NSString *methodName = WFSTR(@"classForProperty%@", [property stringByReplacingCharactersInRange:NSMakeRange(0, 1)
                                                                                          withString:[[property substringToIndex:1] uppercaseString]]);
    SEL selector = NSSelectorFromString(methodName);
    if (selector && [self respondsToSelector:selector])
        class = objc_msgSend(self, selector);
    
    if (class)
        return class;
    
    NSArray *attrs = [attributes componentsSeparatedByString:@","];
    for (NSString *attribute in attrs)
    {
        if ([attribute hasPrefix:@"T"])
        {
            type = [attribute substringFromIndex:1];
        }
    }
    
    if ([type length] > 3)
    {
        NSString *className = [type substringWithRange:NSMakeRange(2, [type length] - 3)];
        //WFLog(@"Class type %@ occurred for property address@hidden", className, property);
        class = NSClassFromString(className);
        //if (!class)
        //    NSLog(@"WARNING: Class type %@ asked for my broperty %@ not found.", className, property);
    }
    
    return class;
}

- (NSDictionary *)dictionaryRepresentation
{
    // Get a list of properties.
    unsigned int propertyCount = 0;
    objc_property_t *properties = class_copyPropertyList([self class], &propertyCount);
    NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:propertyCount];
    
    if (properties)
    {
        // Enumerate all properties.
        
        for (unsigned int i = 0; i < propertyCount; i++)
        {
            objc_property_t property = properties[i];
            NSString *name = @(property_getName(property)); // Property name.
                                                            //WFLog(@"Accessing property address@hidden", name);
            
#if defined(GNUSTEP)
            if ([name hasPrefix:@"_end"])
                continue;
#endif
            
            id value = nil;
            
            value = [self valueForKey:name]; // Find the value, using KVO.
            
            if (!value)
            {
                //NSLog(@"WARNING: Cannot determine value for property %@, Skipped.", name);
                continue;
            }
            
            if ([name isEqualToString:@"ID"])
            {
                name = @"id"; // ID is used instead of id.
            }
            
            if ([value isKindOfClass:[WFObject class]])
            {
                value = [value dictionaryRepresentation];
            }
            else if ([value isKindOfClass:[NSArray class]])
            {
                NSMutableArray *outputArray = [NSMutableArray arrayWithCapacity:[value count]];
                for (id object in value)
                {
                    if ([object isKindOfClass:[WFObject class]])
                    {
                        [outputArray addObject:[object dictionaryRepresentation]];
                    }
                    else
                    {
                        [outputArray addObject:object];
                    }
                }
            }
            
            dictionary[name] = value;
        }
        free(properties);
        properties = NULL;
    }
    return dictionary;
}

@end

@implementation WFObject (WFObjectJSON)

- (id)initWithJSONData:(NSData *)data error:(NSError *__autoreleasing *)error
{
    NSError *err = nil;
    id object = [NSJSONSerialization JSONObjectWithData:data
                                                options:0
                                                  error:&err];
    if (!object)
    {
        WFAssignPointer(error, err);
        return nil;
    }
    
    return [self initWithDictionary:object];
}

- (BOOL)canRepresentInJSON
{
    return [NSJSONSerialization isValidJSONObject:[self dictionaryRepresentation]];
}

- (NSData *)JSONDataWithError:(NSError *__autoreleasing *)error
{
    NSError *err = nil;
    NSDictionary *dict = [self dictionaryRepresentation];
    
    if (!dict)
    {
        NSDictionary *userInfo = @{
                                   NSLocalizedDescriptionKey:
                                       NSLocalizedStringFromTableInBundle(@"err.noval", @"error", WFThisBundle, @"")
                                   };
        err = [NSError errorWithDomain:WFErrorDoamin
                                  code:1
                              userInfo:userInfo];
        WFAssignPointer(error, err);
        return nil;
    }
    
    NSData *data = "" dataWithJSONObject:[self dictionaryRepresentation]
                                                   options:0
                                                     error:&err];
    
    if (!data)
    {
        WFAssignPointer(error, err);
        return nil;
    }
    
    return data;
}

@end

@implementation WFObject (WFEquality)

- (BOOL)isEqual:(id)object
{
    if ([self respondsToSelector:@selector(ID)] && [object respondsToSelector:@selector(ID)])
        return [[self ID] isEqual:[object ID]];
    else
        return [super isEqual:object];
}

@end

WFLogin.h

//
//  WFLogin.h
//  FusionKit.D
//
//  Created by Maxthon Chan on 13-4-8.
//  Copyright (c) 2013 myWorld Creations. All rights reserved.
//

#import <FusionKit/FusionKit.h>

WFBeginDecls

@class WFWrapper;

@interface WFLogin : WFObject

@property NSString *user;
@property NSString *pass;

WFEndProperties;

+ (BOOL)loginAsUser:(NSString *)username withPassword:(NSString *)password error:(NSError **)error;

- (WFWrapper *)login;

@end

WFEndDecls

WFLogin.m

//
//  WFLogin.m
//  FusionKit.D
//
//  Created by Maxthon Chan on 13-4-8.
//  Copyright (c) 2013 myWorld Creations. All rights reserved.
//

#import "WFLogin.h"

@implementation WFLogin

- (Class)classForMethodLogin
{
    return [WFWrapper class];
}

+ (BOOL)loginAsUser:(NSString *)username withPassword:(NSString *)password error:(NSError *__autoreleasing *)error
{
    WFLogin *login = [[WFLogin alloc] init];
    login.user = username;
    login.pass = password;
    id status = [login login];
    if ([status isKindOfClass:[WFWrapper class]])
        return [status boolValue];
    else if (status)
    {
        WFAssignPointer(error, status);
        return NO;
    }
    else
        return NO;
}

@end

Create a WFLogin object and fill in both its properties with string, then call [WFLogin dictionaryRepresentation] and you will see the bug.

在 2013-4-10,上午12:13,David Chisnall <address@hidden> 写道:

Please send me a (simple) test case.

David

On 9 Apr 2013, at 17:10, Maxthon Chan <address@hidden> wrote:

When I enumerate a class for all its properties and querying its name, the name of last property is always cobbled. I have to use some "quick & dirty" way (i.e add a dummy property at the end) to prevent this from affecting. OS X does not have this issue, though.

Can anyone figure out what is wrong?
_______________________________________________
Gnustep-dev mailing list
address@hidden
https://lists.gnu.org/mailman/listinfo/gnustep-dev




reply via email to

[Prev in Thread] Current Thread [Next in Thread]