| // 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 |