1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
// 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
|