forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce ObjCPropertyReleaser for automatic releasing of Objective-C
properties marked "retain" or "copy" backed by synthesized instance variables. BUG=none TEST=base_unittests ObjCPropertyReleaserTest.SesameStreet Review URL: http://codereview.chromium.org/6881091 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@82383 0039d316-1c4b-4281-b951-d872f2087c98
- Loading branch information
mark@chromium.org
committed
Apr 20, 2011
1 parent
bd680cb
commit 10756c5
Showing
5 changed files
with
474 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
// 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. | ||
|
||
#ifndef BASE_MAC_OBJC_PROPERTY_RELEASER_H_ | ||
#define BASE_MAC_OBJC_PROPERTY_RELEASER_H_ | ||
#pragma once | ||
|
||
#import <Foundation/Foundation.h> | ||
|
||
namespace base { | ||
namespace mac { | ||
|
||
// ObjCPropertyReleaser is a C++ class that can automatically release | ||
// synthesized Objective-C properties marked "retain" or "copy". The expected | ||
// use is to place an ObjCPropertyReleaser object within an Objective-C class | ||
// definition. When built with the -fobjc-call-cxx-cdtors compiler option, | ||
// the ObjCPropertyReleaser's destructor will be called when the Objective-C | ||
// object that owns it is deallocated, and it will send a -release message to | ||
// the instance variables backing the appropriate properties. If | ||
// -fobjc-call-cxx-cdtors is not in use, ObjCPropertyReleaser's | ||
// ReleaseProperties method can be called from -dealloc to achieve the same | ||
// effect. | ||
// | ||
// Example usage: | ||
// | ||
// @interface AllaysIBF : NSObject { | ||
// @private | ||
// NSString* string_; | ||
// NSMutableDictionary* dictionary_; | ||
// NSString* notAProperty_; | ||
// IBFDelegate* delegate_; // weak | ||
// | ||
// // It's recommended to put the class name into the property releaser's | ||
// // instance variable name to gracefully handle subclassing, where | ||
// // multiple classes in a hierarchy might want their own property | ||
// // releasers. | ||
// base::mac::ObjCPropertyReleaser propertyReleaser_AllaysIBF_; | ||
// } | ||
// | ||
// @property(retain, nonatomic) NSString* string; | ||
// @property(copy, nonatomic) NSMutableDictionary* dictionary; | ||
// @property(assign, nonatomic) IBFDelegate* delegate; | ||
// @property(retain, nonatomic) NSString* autoProp; | ||
// | ||
// @end // @interface AllaysIBF | ||
// | ||
// @implementation AllaysIBF | ||
// | ||
// @synthesize string = string_; | ||
// @synthesize dictionary = dictionary_; | ||
// @synthesize delegate = delegate_; | ||
// @synthesize autoProp; | ||
// | ||
// - (id)init { | ||
// if ((self = [super init])) { | ||
// // Initialize with [AllaysIBF class]. Never use [self class] because | ||
// // in the case of subclassing, it will return the most specific class | ||
// // for |self|, which may not be the same as [AllaysIBF class]. This | ||
// // would cause AllaysIBF's -.cxx_destruct or -dealloc to release | ||
// // instance variables that only exist in subclasses, likely causing | ||
// // mass disaster. | ||
// propertyReleaser_AllaysIBF_.Init(self, [AllaysIBF class]); | ||
// } | ||
// return self; | ||
// } | ||
// | ||
// @end // @implementation AllaysIBF | ||
// | ||
// When an instance of AllaysIBF is deallocated, the ObjCPropertyReleaser will | ||
// send a -release message to string_, dictionary_, and the compiler-created | ||
// autoProp instance variables. No -release will be sent to delegate_ as it | ||
// is marked "assign" and not "retain" or "copy". No -release will be sent to | ||
// notAProperty_ because it doesn't correspond to any declared @property. | ||
// | ||
// Another way of doing this would be to provide a base class that others can | ||
// inherit from, and to have the base class' -dealloc walk the property lists | ||
// of all subclasses in an object to send the -release messages. Since this | ||
// involves a base reaching into its subclasses, it's deemed scary, so don't | ||
// do it. ObjCPropertyReleaser's design ensures that the property releaser | ||
// will only operate on instance variables in the immediate object in which | ||
// the property releaser is placed. | ||
|
||
class ObjCPropertyReleaser { | ||
public: | ||
// ObjCPropertyReleaser can only be owned by an Objective-C object, so its | ||
// memory is always guaranteed to be 0-initialized. Not defining the default | ||
// constructor can prevent an otherwise no-op -.cxx_construct method from | ||
// showing up in Objective-C classes that contain a ObjCPropertyReleaser. | ||
|
||
// Upon destruction (expected to occur from an Objective-C object's | ||
// -.cxx_destruct method), release all properties. | ||
~ObjCPropertyReleaser() { | ||
ReleaseProperties(); | ||
} | ||
|
||
// Initialize this object so that it's armed to release the properties of | ||
// object |object|, which must be of type |classy|. The class argument must | ||
// be supplied separately and cannot be gleaned from the object's own type | ||
// because an object will allays identify itself as the most-specific type | ||
// that describes it, but the ObjCPropertyReleaser needs to know which class | ||
// type in the class hierarchy it's responsible for releasing properties | ||
// for. For the same reason, Init must be called with a |classy| argument | ||
// initialized using a +class (class) method such as [MyClass class], and | ||
// never a -class (instance) method such as [self class]. | ||
// | ||
// -.cxx_construct can only call the default constructor, but | ||
// ObjCPropertyReleaser needs to know about the Objective-C object that owns | ||
// it, so this can't be handled in a constructor, it needs to be a distinct | ||
// Init method. | ||
void Init(id object, Class classy); | ||
|
||
// Release all of the properties in object_ defined in class_ as either | ||
// "retain" or "copy" and with an identifiable backing instance variable. | ||
// Properties must be synthesized to have identifiable instance variables. | ||
void ReleaseProperties(); | ||
|
||
private: | ||
id object_; | ||
Class class_; | ||
}; | ||
|
||
} // namespace mac | ||
} // namespace base | ||
|
||
#endif // BASE_MAC_OBJC_PROPERTY_RELEASER_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
// 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 "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 NULL. | ||
const char* 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); | ||
|
||
bool releasable = false; | ||
while (*property_attributes) { | ||
switch (*property_attributes) { | ||
// 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': | ||
// 'V' is specified as the last attribute to occur, so if releasable | ||
// wasn't set to true already, it won't be set to true now. Since 'V' | ||
// is last, its value up to the end of the attribute string is the | ||
// name of the instance variable backing the property. | ||
if (!releasable || !*++property_attributes) { | ||
return NULL; | ||
} | ||
return property_attributes; | ||
} | ||
char previous_char = *property_attributes; | ||
while (*++property_attributes && previous_char != ',') { | ||
previous_char = *property_attributes; | ||
} | ||
} | ||
return NULL; | ||
} | ||
|
||
} // namespace | ||
|
||
void ObjCPropertyReleaser::Init(id object, Class classy) { | ||
DCHECK(!object_); | ||
DCHECK(!class_); | ||
DCHECK([object isKindOfClass:classy]); | ||
|
||
object_ = object; | ||
class_ = classy; | ||
} | ||
|
||
void ObjCPropertyReleaser::ReleaseProperties() { | ||
CHECK(object_); | ||
CHECK(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]; | ||
const char* instance_name = ReleasableInstanceName(property); | ||
if (instance_name) { | ||
id instance_value = nil; | ||
object_getInstanceVariable(object_, instance_name, | ||
(void**)&instance_value); | ||
[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 |
Oops, something went wrong.