I override object's -(NSString*)description however Xcode always displays error: summary string parsing error in summary field in variables view.
My current implementation is the following:
- (NSString*)description {
return [NSString stringWithFormat:#"<%# %p> x=%f, y=%f", self.class, self, _x, _y];
}
If I type po objectName in console, LLDB shows a fine output as expected, however Xcode and command p objectName always indicate error, so what's the proper debug description format to make summary field work? Worth to notice that the output of "p" command is the same as a summary message that you see in Xcode for instances of Foundation classes.
Update:
As far as I can see from "WWDC 2012 session Debugging in Xcode", custom summaries can be implemented using Custom python script only. -(NSString*)description or -(NSString*)debugDescription methods are not connected anyhow to summary messages. I thought they are because I got an error displayed, but it seems it's a standard message for classes that do not have their own formatters.
I would suggest at least:
- (NSString*)description {
return [NSString stringWithFormat:#"%#; x=%f, y=%f", [super description], _x, _y];
}
So that you're not manually replicating the NSObject default and thereby blocking any non-default behaviour your superclass may have opted to include.
Beyond that, "summary string parsing error" is an lldb error. It's being reported by the debugger only. Per its documentation, po is correct for Objective-C objects; p is for C or C++ objects. So you needn't heed that error — it's essentially just telling you that you used the wrong lldb command.
EDIT: for what it's worth, the method used by CFArray is open source and looks like:
static CFStringRef __CFArrayCopyDescription(CFTypeRef cf) {
CFArrayRef array = (CFArrayRef)cf;
CFMutableStringRef result;
const CFArrayCallBacks *cb;
CFAllocatorRef allocator;
CFIndex idx, cnt;
cnt = __CFArrayGetCount(array);
allocator = CFGetAllocator(array);
result = CFStringCreateMutable(allocator, 0);
switch (__CFArrayGetType(array)) {
case __kCFArrayImmutable:
CFStringAppendFormat(result, NULL, CFSTR("<CFArray %p [%p]>{type = immutable, count = %u, values = (%s"), cf, allocator, cnt, cnt ? "\n" : "");
break;
case __kCFArrayDeque:
CFStringAppendFormat(result, NULL, CFSTR("<CFArray %p [%p]>{type = mutable-small, count = %u, values = (%s"), cf, allocator, cnt, cnt ? "\n" : "");
break;
}
cb = __CFArrayGetCallBacks(array);
for (idx = 0; idx < cnt; idx++) {
CFStringRef desc = NULL;
const void *val = __CFArrayGetBucketAtIndex(array, idx)->_item;
if (NULL != cb->copyDescription) {
desc = (CFStringRef)INVOKE_CALLBACK1(cb->copyDescription, val);
}
if (NULL != desc) {
CFStringAppendFormat(result, NULL, CFSTR("\t%u : %#\n"), idx, desc);
CFRelease(desc);
} else {
CFStringAppendFormat(result, NULL, CFSTR("\t%u : <%p>\n"), idx, val);
}
}
CFStringAppend(result, CFSTR(")}"));
return result;
}
As with the other comments above, I'm willing to gamble that the answer is: Xcode's debugger isn't smart in any sense and definitely isn't smart enough to use the correct po means of getting an Objective-C description; if your object is an uninflected Objective-C object then the debugger isn't going to be able to figure it out.
Related
In my program, I store objective-c objects in a c array, like this
va_start(list, o);
retval->objs = malloc(SIZE * count);
retval->objs[0] = (__bridge void *)o;
for (int i = 1; i < count; i++)
{
id o = va_arg(list, id);
retval->objs[i] = (__bridge void *)o;
}
va_end(list);
(count is a number containing how many objects will be added; that value is always correct)
objs is a void ** and is part of retval, which is a pointer to a struct. As of now, SIZE is defined as 100. Increasing and decreasing that had no effect.
As you can see, I bridge o to a void *, as I have to. objs, when all the objects are added, contains 3 objective-c objects. When I try to access a value like this
void *obj = CLArrayObjectAtIndex(_arr, ind);
return (__bridge id)obj;
this is the CLArrayObjectAtIndex() function
void *CLArrayObjectAtIndex(CLArrayType *arr, int ind)
{
void *o = arr->objs[ind];
if (o)
return o;
else
perror("Attempt to access NULL object or index out of bounds."), abort();
}
if the index (ind) is 0, it works. If the index is 1, the program crashes when it returns in main. If the index is 2, the program crashes as soon as I try to access it. If the index is 1, the value returned above is correct, but when the program crashes on return it is nil.
If the index is 1, the EXC_BAD_ACCESS code is 1; if the index is 2, the code is EXC_I386_GPFLT, a general protection fault. I already checked here for an explanation of this exception, although I couldn't find anything helpful. So, does anybody see why this error may be occurring?
when you store obj-c objects in C array don't just bridge cast them since that way arc doesn't know they are still used and releases them. __bridge_retain them so they stay around later, when you free the array __bridge_transfer them to give them back to ARC
also don't define size as 100.. sizeof(id) should work. You only need to store pointers
NOTE: the beginning of this question is similar (the first part is the same) as this one: LINK
However, the final question is completely different.
I'm implementing a "Code Injector Class", that through method swizzling can give you the possibility to do something like this:
FLCodeInjector *injector = [FLCodeInjector injectorForClass:[self class]];
[injector injectCodeBeforeSelector:#selector(aSelector:) code:^{
NSLog(#"This code should be injected");
}];
aSelector can be a method with variable number of arguments, and variable return type. Arguments / and return type can be objects or primitive type.
First, I attach the code of injectCodeBeforeSelector: to let you understand what I'm doing (I removed not interesting parts of the code):
- (void)injectCodeBeforeSelector:(SEL)method code:(void (^)())completionBlock
{
NSString *selector = NSStringFromSelector(method);
[self.dictionaryOfBlocks setObject:completionBlock forKey:selector];
NSString *swizzleSelector = [NSString stringWithFormat:#"SWZ%#", selector];
//NSMethodSignature *signature = [self.mainClass instanceMethodSignatureForSelector:method];
// add a new method to the swizzled class
Method origMethod = class_getInstanceMethod(self.mainClass, NSSelectorFromString(selector));
const char *encoding = method_getTypeEncoding(origMethod);
[self addSelector:NSSelectorFromString(swizzleSelector) toClass:self.mainClass originalSelector:method methodTypeEncoding:encoding];
SwizzleMe(self.mainClass, NSSelectorFromString(selector), NSSelectorFromString(swizzleSelector));
}
-(void)addSelector:(SEL)selector toClass:(Class)aClass originalSelector:(SEL)originalSel methodTypeEncoding:(const char *)encoding
{
//NSMethodSignature *signature = [aClass methodSignatureForSelector:originalSel];
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:encoding];
const char *type = [signature methodReturnType];
IMP implementation = (IMP)intGenericFunction;
if (strcmp(#encode(id), type) == 0) {
// the argument is an object
implementation = objectGenericFunction;
}
else if (strcmp(#encode(int), type) == 0)
{
// the argument is an int
implementation = (IMP)intGenericFunction;
}
else if (strcmp(#encode(long), type) == 0)
{
// the argument is a long
implementation = (IMP)longGenericFunction;
}
else if (strcmp(#encode(double), type) == 0)
{
// the argument is double
implementation = (IMP)doubleGenericFunction;
}
else if (strcmp(#encode(float), type) == 0)
{
// the argument is float
implementation = (IMP)floatGenericFunction;
}
else
{
// the argument is char or others
implementation = (IMP)intGenericFunction;
}
class_addMethod(aClass,
selector,
implementation, encoding);
}
What is happening here? Basically, basing on the expected return type of the original selector, I add a new method to the object with the correct return type, then apply the swizzle.
All is working correctly, but I'd like to know if it's possible to "compact" the following code (some syntax that I don't know or something I'm missing), because for each return type I have a function that is almost identical to the others, only the returned type is different.
I attach two of them as an example:
int intGenericFunction(id self, SEL cmd, ...) {
FLCodeInjector *injector = [FLCodeInjector injectorForClass:[self class]];
[injector executeBlockForSelector:cmd];
va_list arguments, copiedArguments;
va_start ( arguments, cmd );
va_copy(copiedArguments, arguments);
va_end(arguments);
void * returnValue = getReturnValue(self, cmd, copiedArguments);
int returnedInt = *(int *)returnValue;
return returnedInt;
}
double doubleGenericFunction(id self, SEL cmd, ...) {
FLCodeInjector *injector = [FLCodeInjector injectorForClass:[self class]];
[injector executeBlockForSelector:cmd];
va_list arguments, copiedArguments;
va_start ( arguments, cmd );
va_copy(copiedArguments, arguments);
va_end(arguments);
void * returnValue = getReturnValue(self, cmd, copiedArguments);
double returnedDouble = *(double *)returnValue;
return returnedDouble;
}
As you can see, the functions are almost identical, the only different is the CAST before the return, and the return type of the function.
I'm implementing it in the correct way, or there are more efficient way to do it?
Thanks
You're correct that you'll need to write a different IMP for each return type, at least unless you drop down to assembly to do the dispatch, the way objc_msgSend does. (Even that function requires a couple different type variants, though.) However, if the difference truly is just a couple of type names, you may be able to define a macro that reduces the boilerplate:
// This macro syntax is off the top of my head; it may not be correct.
#define GENERIC_FUNCTION_FOR_TYPE(type) type type##GenericFunction(id self, SEL cmd, ...) { \
...other lines omitted... \
type returnedValue = *(type *)returnValue; \
return returnedValue; \
}
GENERIC_FUNCTION_FOR_TYPE(int)
GENERIC_FUNCTION_FOR_TYPE(double)
...etc...
i have an issue when disconnecting ethernet-cable from computer or just turned off ethernet. in this case some entrys do not exist and my app would crash.
so i tryed to find out how to prevent and just found CFDictionaryContainsKey, but this does not prevent the error. Anybody who knows an workaround which is also working lower than osx 10.6 ?
- (NSString *)checkNetworkInterface
{
SCDynamicStoreRef ds = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("myapp"), NULL, NULL);
CFDictionaryRef dr = SCDynamicStoreCopyValue(ds, CFSTR("State:/Network/Global/IPv4"));
Boolean ck = CFDictionaryContainsKey( dr, CFSTR("PrimaryInterface"));
NSString *interfaceString;
if (ck) {
CFStringRef interface = CFDictionaryGetValue(dr, CFSTR("PrimaryInterface"));
interfaceString = [NSString stringWithString:( NSString *)interface ];
} else {
interfaceString = [NSString stringWithString:#"" ];
}
CFRelease(dr);
CFRelease(ds);
return interfaceString;
}
if "State:/Network/Global/IPv4" does not exist, app crashes :(
As the documentation for SCDynamicStoreCopyValue() states:
Return Value: The value associated with the specified key, or NULL if no value was located or if an error occurred. You must release the returned value.
CFDictionaryContainsKey() attempts to inspect the passed-in dictionary; if it's NULL, you crash with a NULL pointer dereference. You also shouldn't CFRelease() a NULL pointer.
To correct this, just add a NULL check before calling CFDictionaryContainsKey().
NSString *interfaceString;
if(dr != NULL && CFDictionaryContainsKey(dr, CFSTR("PrimaryInterface")))
{
CFStringRef interface = CFDictionaryGetValue(dr, CFSTR("PrimaryInterface"));
...
CFRelease(dr);
}
I think you just want to check whether dr == NULL and abort if so. Apologies if this is a little obvious, but you're not doing it here and it seems like it would ward off the crash.
m_cAppIdMap is an object of a dictionary.
I want to iterate through the dictionary and to ind and remove the value pEvent.wTimerId is an unsigned short integer that is stored as key in the dictionary.
if(unsigned short* key in m_cAppIdMap) //error:Expected expression before 'unsigned'
{
(void)[self findAndRemoveEvent:pEvent];
(void)CFDictionaryRemoveValue(m_cAppIdMap,&wTimerId);
free(pEvent);
bReturn = YES;
}
I am getting an error when i try to iterate through the loop.
EDITED
-(BOOL)KillTimer:(unsigned short)wTimerId
{
stRs232Timer* pEvent;
BOOL bReturn=FALSE;
theLock = [[NSLock alloc]init];
if ([theLock tryLock]) {
// if ( m_cAppIdMap.Lookup(wTimerId,pEvent) )
// {
// (void)findAndRemoveEvent(pEvent); // remove from event queue
// (void)m_cAppIdMap.RemoveKey(wTimerId); // remove from app map
for(wTimerId in m_cAppIdMap)
{
(void)[self findAndRemoveEvent:pEvent];
(void)CFDictionaryRemoveValue(m_cAppIdMap,&wTimerId);
free(pEvent);
bReturn = YES;
}
[theLock unlock];
}
return bReturn;
}
I am getting error in this code 'selector element does not have a valid object type' . I need to search for wTimerId(key) in the m_cAppIdMap. Is it what i'm doing is correct.The commented lines above the for loop is the implementation of the same code in cpp. I coud not make the same logic over here in Objective C.
I think you meant to use for rather than if. Additionally, the fast enumeration syntax
for (x in y) can only be used on objects that implement the NSFastEnumeration protocol—typically NSArray. It looks like you're using C arrays, so this syntax won't work anyway.
you meant to write for (VARIABLE in CONTAINER) {...} -- but your sample uses if, not for.
side note: it is an error to mutate the collections you iterate over during the iteration.
This must be very simple, but I can't figure out how to do this: I have a C-function to monitor current memory usage:
natural_t report_memory(void) {
struct task_basic_info info;
mach_msg_type_number_t size = sizeof(info);
kern_return_t kerr = task_info(mach_task_self(),
TASK_BASIC_INFO,
(task_info_t)&info,
&size);
if( kerr == KERN_SUCCESS ) {
return info.resident_size;
} else {
NSLog(#"Error with task_info(): %s", mach_error_string(kerr));
return 0;
}
}
Now, I would like to use it. How do I declare it in the .h?
I tried the (for me) obvious within the objective c methods:
natural_t report_memory(void);
Calling this somewhere in the code:
NSLog(#"Memory used: %u", rvC.report_memory());
The Compiler complains error: called object is not a function. Thus, I assume, the declaration is somehow wrong. I tried several options, but the best I could get was a runtime error...
How to fix this?
rvC.report_memory()
should be replaced with
report_memory()
since it is a C function.
If you want to use this function in other modules, you should also put in your header (.h) file this line
extern natural_t report_memory(void);