extern pascal void CFQPropertyListDeepApplyFunction(CFPropertyListRef propList, CFQPropertyListDeepApplierFunction func, void *context) // See comment in header. { assert(propList != NULL); assert(func != NULL); // Call "func" for this node. func(propList, context); // If this node is a dictionary or an array, call func for // each element. if ( CFGetTypeID(propList) == CFDictionaryGetTypeID() ) { CFIndex count; CFIndex index; count = CFDictionaryGetCount( (CFDictionaryRef) propList); if (count > 0) { const void **keys; keys = (const void **) malloc( count * sizeof(const void *)); if (keys != NULL) { CFDictionaryGetKeysAndValues( (CFDictionaryRef) propList, keys, NULL); for (index = 0; index < count; index++) { CFQPropertyListDeepApplyFunction(CFDictionaryGetValue( (CFDictionaryRef) propList, keys[index]), func, context); } free(keys); } } } else if ( CFGetTypeID(propList) == CFArrayGetTypeID() ) { CFIndex count; long index; count = CFArrayGetCount( (CFArrayRef) propList); for (index = 0; index < count; index++) { CFQPropertyListDeepApplyFunction(CFArrayGetValueAtIndex( (CFArrayRef) propList, index), func, context); } } }
static SInt32 TotalAllRefCounts(SCPreferencesRef prefsRef, CFArrayRef allKeys) // Given a connection to the SCF preferences database and an array // of preference keys, this routine calculates the total of all // of the reference counts of all of the nodes in all of the SCF // preferences. I use this routine to check for reference count leaks // in my use of the SCF preferences database. { CFIndex keyCount; CFIndex keyIndex; SInt32 result; result = 0; keyCount = CFArrayGetCount(allKeys); for (keyIndex = 0; keyIndex < keyCount; keyIndex++) { CFPropertyListRef thisPref; thisPref = SCPreferencesGetValue(prefsRef, (CFStringRef) CFArrayGetValueAtIndex(allKeys, keyIndex)); // C++ requires cast CFQPropertyListDeepApplyFunction(thisPref, RefCounter, (void *) &result ); } return result; }
static void LeakTest(TestFunc tester) // Given a test name and a pointer to a test function, // this routine calls the test function repeatedly to check // for SCF preferences reference count leaks. { OSStatus err; SCPreferencesRef prefsRef; CFArrayRef allKeys; int i; SInt32 startCount; SInt32 endCount; // This is kinda cheesy. We need to use the same SCPreferencesRef // that MoreSCF uses because otherwise we can't see the reference // count changes done by MoreSCF. Ideally, we wouldn't want to run // within a MoreSCOpen/MoreSCClose pair because then our changes // aren't necessarily being committed to the database. However, // given the current MoreSCF architecture, where the SCPreferenceRef // is only valid inside the MoreSCOpen/MoreSCClose pair, that's // exactly what we have to do. allKeys = NULL; err = MoreSCOpen(false, false); if (err == noErr) { prefsRef = MoreSCGetSCPreferencesRef(); } // Now get a copy of the array of all keys in the database. // We make this copy using CFPropertyListCreateDeepCopy so we // know that it's completely independent of the SCF. if (err == noErr) { CFArrayRef allKeysOrig; allKeysOrig = NULL; allKeysOrig = SCPreferencesCopyKeyList(prefsRef); if (allKeysOrig == NULL) { err = SCError(); } if (err == noErr) { allKeys = (CFArrayRef) CFPropertyListCreateDeepCopy(NULL, allKeysOrig, kCFPropertyListMutableContainersAndLeaves); // C++ requires cast if (allKeys == NULL) { err = coreFoundationUnknownErr; } } CFQRelease(allKeysOrig); } // Now do the reference counting test. Call the tester function // a few times to allow the refcounts to stabilise. Then get // a summ of all the nodes in all of the keys in SCF. Then // run the test a 10 more times and get another count. If // the counts are different, we're in trouble. if (err == noErr) { for (i = 0; i < 3; i++) { tester(); } startCount = TotalAllRefCounts(prefsRef, allKeys); for (i = 0; i < 10; i++) { tester(); } endCount = TotalAllRefCounts(prefsRef, allKeys); if (startCount != endCount) { CFIndex keyCount; CFIndex keyIndex; fprintf(stderr, "*** Leaked %ld reference counts.\n", endCount - startCount); keyCount = CFArrayGetCount(allKeys); for (keyIndex = 0; keyIndex < keyCount; keyIndex++) { CFPropertyListRef thisPref; // The commented out code is only needed when you're actively // trying to track down a leak. Given that leaks are rare // I decided against making a proper architecture for this. // Just uncomment the code and modify it appropriately. startCount = 0; thisPref = SCPreferencesGetValue(prefsRef, (CFStringRef) CFArrayGetValueAtIndex(allKeys, keyIndex)); // C++ requires cast CFQPropertyListDeepApplyFunction(thisPref, RefCounter, (void *) &startCount ); // if (keyIndex == 0) { // fprintf(stderr, "*** BEFORE ***\n"); // PrintPropertyList(thisPref); // } tester(); endCount = 0; thisPref = SCPreferencesGetValue(prefsRef, (CFStringRef) CFArrayGetValueAtIndex(allKeys, keyIndex)); // C++ requires cast CFQPropertyListDeepApplyFunction(thisPref, RefCounter, (void *) &endCount ); // if (keyIndex == 0) { // fprintf(stderr, "*** AFTER ***\n"); // PrintPropertyList(thisPref); // } if (startCount != endCount) { fprintf(stderr, "*** Leaked %ld reference counts in set number %ld\n", endCount - startCount, keyIndex); CFShow(CFArrayGetValueAtIndex(allKeys, keyIndex)); } } } } MoreSCClose(&err, false); CFQRelease(allKeys); if (err != noErr) { fprintf(stderr, "*** Failed with error %ld!\n", err); } }