// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #import "base/mac/objc_property_releaser.h" #import <objc/runtime.h> #include <stdlib.h> #include <string> #include "base/logging.h" namespace base { namespace mac { namespace { // Returns the name of the instance variable backing the property, if known, // if the property is marked "retain" or "copy". If the instance variable name // is not known (perhaps because it was not automatically associated with the // property by @synthesize) or if the property is not "retain" or "copy", // returns an empty string. std::string ReleasableInstanceName(objc_property_t property) { // TODO(mark): Starting in newer system releases, the Objective-C runtime // provides a function to break the property attribute string into // individual attributes (property_copyAttributeList), as well as a function // to look up the value of a specific attribute // (property_copyAttributeValue). When the SDK defining that interface is // final, this function should be adapted to walk the attribute list as // returned by property_copyAttributeList when that function is available in // preference to scanning through the attribute list manually. // The format of the string returned by property_getAttributes is documented // at // http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/TP40008048-CH101-SW6 const char* property_attributes = property_getAttributes(property); std::string instance_name; bool releasable = false; while (*property_attributes) { char name = *property_attributes; const char* value = ++property_attributes; while (*property_attributes && *property_attributes != ',') { ++property_attributes; } switch (name) { // It might seem intelligent to check the type ('T') attribute to verify // that it identifies an NSObject-derived type (the attribute value // begins with '@'.) This is a bad idea beacuse it fails to identify // CFTypeRef-based properties declared as __attribute__((NSObject)), // which just show up as pointers to their underlying CFType structs. // // Quoting // http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProperties.html#//apple_ref/doc/uid/TP30001163-CH17-SW27 // // > In Mac OS X v10.6 and later, you can use the __attribute__ keyword // > to specify that a Core Foundation property should be treated like // > an Objective-C object for memory management: // > @property(retain) __attribute__((NSObject)) CFDictionaryRef // > myDictionary; case 'C': // copy case '&': // retain releasable = true; break; case 'V': // instance variable name // 'V' is specified as the last attribute to occur in the // documentation, but empirically, it's not always the last. In // GC-supported or GC-required code, the 'P' (GC-eligible) attribute // occurs after 'V'. instance_name.assign(value, property_attributes - value); break; } if (*property_attributes) { ++property_attributes; } } if (releasable) { return instance_name; } return std::string(); } } // namespace void ObjCPropertyReleaser::Init(id object, Class classy) { DCHECK(!object_); DCHECK(!class_); CHECK([object isKindOfClass:classy]); object_ = object; class_ = classy; } void ObjCPropertyReleaser::ReleaseProperties() { DCHECK(object_); DCHECK(class_); unsigned int property_count = 0; objc_property_t* properties = class_copyPropertyList(class_, &property_count); for (unsigned int property_index = 0; property_index < property_count; ++property_index) { objc_property_t property = properties[property_index]; std::string instance_name = ReleasableInstanceName(property); if (!instance_name.empty()) { id instance_value = nil; Ivar instance_variable = object_getInstanceVariable(object_, instance_name.c_str(), (void**)&instance_value); DCHECK(instance_variable); [instance_value release]; } } free(properties); // Clear object_ and class_ in case this ObjCPropertyReleaser will live on. // It's only expected to release the properties it supervises once per Init. object_ = nil; class_ = nil; } } // namespace mac } // namespace base