如何访问类扩展中声明的变量?

时间:2012-04-25 00:06:39

标签: objective-c lldb

MyClass.h

@interface MyClass : NSObject
@end

MyClass.m

// Define a private variable in a class extension
@interface MyClass () {
    NSString *name;
}
@end

然后在lldb:

(lldb) po myClassInstance->name
error: 'MyClass' does not have a member named 'name'
error: 1 errors parsing expression

那么如何在调试器中访问该变量?

使用xcode 4.3.2

谢谢!

3 个答案:

答案 0 :(得分:6)

(lldb) po [myClassInstance valueForKey:@"name"]

答案 1 :(得分:0)

直接干净地访问这些实例变量的唯一方法是通过Objective-C运行时,它提供了有用的函数object_getInstanceVariable。该值通过引用传递回来,可以是许多不同的类型,因此它在调试器中不是很有用。但是你的问题激励我提出解决方案。

我在NSObject上写了一个类别,允许你从调试器中反省实例变量,而不用担心访问者的副作用。将类别添加到项目后,您可以执行以下操作:

(lldb) po [self valueOfInstanceVariable:@"_name"]
IMG_4078.PNG

以下是代码:

NSObject的+ IvarIntrospection.h

#if DEBUG
#import <Foundation/Foundation.h>

@interface NSObject (IvarIntrospection)

- (id)valueOfInstanceVariable:(NSString *)ivarName;

@end

#endif

NSObject的+ IvarIntrospection.m

#if DEBUG
#import "NSObject+IvarIntrospection.h"
#import <objc/runtime.h>

@implementation NSObject (IvarIntrospection)

- (id)valueOfInstanceVariable:(NSString *)ivarName {
    // Get the value of the instance variable
    // Use a union in order to convert the value to a float or double (see http://en.wikipedia.org/wiki/Type_punning)
    union {
        void *value;
        float f;
        double d;
    } ivar;
    Ivar ivarInfo = object_getInstanceVariable(self, [ivarName UTF8String], &ivar.value);

    // If the instance variable doesn't exist, try adding an underscore
    if (!ivarInfo && ![ivarName hasPrefix:@"_"]) {
        NSString *underscoredIvarName = [@"_" stringByAppendingString:ivarName];
        NSLog(@"Instance variable '%@' does not exist. Perhaps you meant '%@?' Let's try that.", ivarName, underscoredIvarName);

        return [self valueOfInstanceVariable:underscoredIvarName];

    // If there's already an underscore, error
    } else if (!ivarInfo) {
        NSLog(@"Instance variable '%@' does not exist.", ivarName);
        return nil;
    }

    // Figure out what type the instance variable is and return a sensible representation
    const char *type = ivar_getTypeEncoding(ivarInfo);
    switch (type[0]) {
        case 'c':
            return [NSNumber numberWithChar:(char)ivar.value];
        case 'i':
            return [NSNumber numberWithInt:(int)ivar.value];
        case 's':
            return [NSNumber numberWithShort:(short)ivar.value];
        case 'l':
            return [NSNumber numberWithLong:(long)ivar.value];
        case 'q':
            return [NSNumber numberWithLongLong:(long long)ivar.value];
        case 'C':
            return [NSNumber numberWithUnsignedChar:(unsigned char)ivar.value];
        case 'I':
            return [NSNumber numberWithUnsignedInt:(unsigned int)ivar.value];
        case 'S':
            return [NSNumber numberWithUnsignedShort:(unsigned short)ivar.value];
        case 'L':
            return [NSNumber numberWithUnsignedLong:(unsigned long)ivar.value];
        case 'Q':
            return [NSNumber numberWithUnsignedLongLong:(unsigned long long)ivar.value];
        case 'f':
            return [NSNumber numberWithFloat:ivar.f];
        case 'd':
            return [NSNumber numberWithDouble:ivar.d];
        case '*':
            return [NSString stringWithUTF8String:(const char *)ivar.value];
        case '@':
        case '#':
            return (id)ivar.value;
        case ':':
            return NSStringFromSelector((SEL)ivar.value);
        default:
            return [NSValue valueWithBytes:&ivar.value objCType:type];
    }
}

@end

#endif

请注意,编译发布时将自动禁用该类别(感谢调试宏)。

答案 2 :(得分:-1)

如果您需要从name方法之外访问MyClass,则需要定义访问它的方法。您可以编写名为(NSString*) name- (void) setName:(NSString*) newName的方法,但更容易定义属性并合成它们。

MyClass.h 中,您可以定义一个属性。对于字符串,您通常会将它们copy

@interface MyClass : NSObject

@property (copy) NSString* name;

@end

MyClass.m 中,你仍然使用你的界面声明,使用ivar:

@interface MyClass () {
    NSString *name;
}
@end

但是,您还需要合成您的新媒体资源。这将创建用于检索和设置 name 的方法:

@implementation MyClass

@synthesize name = name;

@end

作为惯例,通常在ivar的开头或结尾放置下划线,因此在界面中您将NSString *_name;,并且在实现中您将拥有@synthesize name = _name。这有助于避免在您指的时候意外使用ivar。

您现在可以访问名称属性:

MyClass me = [[[MyClass alloc] init] autorelease];
[me setName:@"My name"];
NSLog(@"Name = %@", [me name]);

Objective-C属性是该语言的一个强大功能,但它们确实有一些您必须学习的怪癖。尝试通过网络搜索“objective-C”,“properties”和“synthesize”的某种组合。

如果您仍然遇到编译错误,请使用您访问name的代码部分编辑您的问题。