Commits

Ronald Oussoren  committed a9c6196

Avoid creating bytes objects when possible on Python 3.3

Starting at Python 3.3 the unicode object stores values
with the minimal byte width (UCS1, UCS2 or UCS4 depending
on the value of the string). Because of this ASCII strings,
such as selector names, can be accessed as 'const char*'
by directly accessing the internal buffer of the unicode
object.

This means we can avoid creating bytes objects for the name,
which should be slightly faster and use less memory. In both
cases this is a micro optimization.

NOTE: There might be more opportunities to apply this shortcut.

  • Participants
  • Parent commits 57fbe1d

Comments (0)

Files changed (6)

File pyobjc-core/Modules/objc/class-builder.m

 
 
 			PyObject* pyname;
-			char*     ocname;
+			const char*     ocname;
 			pyname = key;
+#ifndef PyObjC_FAST_UNICODE_ASCII
 			PyObject* pyname_bytes = NULL;
+#endif
 			if (pyname == NULL) continue;
 
 			if (PyUnicode_Check(pyname)) {
+#ifdef PyObjC_FAST_UNICODE_ASCII
+				ocname = PyObjC_Unicode_Fast_Bytes(pyname);
+				if (ocname == NULL) {
+					goto error_cleanup;
+				}
+#else
 				pyname_bytes = PyUnicode_AsEncodedString(pyname, NULL, NULL);
 				if (pyname_bytes == NULL) {
 					goto error_cleanup;
 					PyErr_SetString(PyExc_ValueError, "empty name");
 					goto error_cleanup;
 				}
+#endif
 #if PY_MAJOR_VERSION == 2
 			} else if (PyString_Check(pyname)) {
 				ocname = PyString_AS_STRING(pyname);
 					py_superclass,
 					protocols);
 				if (new_value == NULL) {
+#ifndef PyObjC_FAST_UNICODE_ASCII
 					Py_CLEAR(pyname_bytes);
+#endif
 					goto error_cleanup;
 				}
 				value = new_value;
 
+#ifndef PyObjC_FAST_UNICODE_ASCII
 				Py_CLEAR(pyname_bytes);
+#endif
 
 				if (PyObjCSelector_Check(value)) {
 					int r;
 					}
 				}
 			}
+#ifndef PyObjC_FAST_UNICODE_ASCII
 			Py_CLEAR(pyname_bytes);
+#endif
 		}
 	}
 

File pyobjc-core/Modules/objc/method-accessor.m

 #include "pyobjc.h"
 
 static PyObject* 
-find_selector(PyObject* self, char* name, int class_method)
+find_selector(PyObject* self, const char* name, int class_method)
 {
 	SEL   sel = PyObjCSelector_DefaultSelector(name);
 	volatile id    objc_object;
 {
 	ObjCMethodAccessor* self = (ObjCMethodAccessor*)_self;
 	PyObject* result = NULL;
+#ifndef PyObjC_FAST_UNICODE_ASCII
 	PyObject* name_bytes;
+#endif
 
 	if (PyUnicode_Check(name)) {
+#ifdef PyObjC_FAST_UNICODE_ASCII
+		if (PyObjC_Unicode_Fast_Bytes(name) == NULL) {
+			return NULL;
+		}
+#else
 		name_bytes = PyUnicode_AsEncodedString(name, NULL, NULL);
 		if (name_bytes == NULL) {
 			return NULL;
 		}
+#endif
 #if PY_MAJOR_VERSION == 2
 	} else if (PyString_Check(name)) {
 		name_bytes = name; Py_INCREF(name_bytes);
 		return NULL;
 	}
 
-	if (strcmp(PyBytes_AsString(name_bytes), "__dict__") == 0) {
+	if (strcmp(
+#ifdef PyObjC_FAST_UNICODE_ASCII
+		PyObjC_Unicode_Fast_Bytes(name),
+#else
+		PyBytes_AsString(name_bytes), 
+#endif
+		"__dict__") == 0) {
+
+#ifndef PyObjC_FAST_UNICODE_ASCII
 		Py_DECREF(name_bytes); name_bytes = NULL;
+#endif
 
 		PyObject* dict;
 		dict = make_dict(self->base, self->class_method);
 		 */
 	}
 
-	if (strcmp(PyBytes_AsString(name_bytes), "__methods__") == 0) {
+	if (strcmp(
+#ifdef PyObjC_FAST_UNICODE_ASCII
+		PyObjC_Unicode_Fast_Bytes(name),
+#else
+		PyBytes_AsString(name_bytes), 
+#endif
+		"__methods__") == 0) {
+
+#ifndef PyObjC_FAST_UNICODE_ASCII
 		Py_DECREF(name_bytes); name_bytes = NULL;
+#endif
 		PyErr_SetString(PyExc_AttributeError,
 			"No such attribute: __methods__");
 		return NULL;
 	}
 
-	if (strcmp(PyBytes_AsString(name_bytes), "__members__") == 0) {
+	if (strcmp(
+#ifdef PyObjC_FAST_UNICODE_ASCII
+		PyObjC_Unicode_Fast_Bytes(name),
+#else
+		PyBytes_AsString(name_bytes), 
+#endif
+		"__members__") == 0) {
+
+#ifndef PyObjC_FAST_UNICODE_ASCII
 		Py_DECREF(name_bytes); name_bytes = NULL;
+#endif
 		PyErr_SetString(PyExc_AttributeError,
 			"No such attribute: __members__");
 		return NULL;
 	}
 
 	if (result != NULL) {
+#ifndef PyObjC_FAST_UNICODE_ASCII
+		Py_DECREF(name_bytes);
+#endif
 		return result;
 	}
 
 	/* Didn't find the selector the first trip around, try harder. */
 	result = find_selector(self->base, 
-		PyBytes_AS_STRING(name_bytes), self->class_method);
+#ifndef PyObjC_FAST_UNICODE_ASCII
+		PyBytes_AS_STRING(name_bytes), 
+#else
+		PyObjC_Unicode_Fast_Bytes(name),
+#endif
+		self->class_method);
 	if (result == NULL) {
+#ifndef PyObjC_FAST_UNICODE_ASCII
 		Py_DECREF(name_bytes); name_bytes = NULL;
+#endif
 		return result;
 	}
 
 		Py_INCREF(self->base);
 
 	}
-	/*Py_XINCREF(((PyObjCSelector*)result)->sel_self);*/
+
+#ifndef PyObjC_FAST_UNICODE_ASCII
 	Py_DECREF(name_bytes);
+#endif
 	return result;
 }
 

File pyobjc-core/Modules/objc/objc-class.m

 
 /* FIXME: This is a lightly modified version of _type_lookup in objc-object.m, need to merge these */
 static inline PyObject*
-_type_lookup(PyTypeObject* tp, PyObject* name, PyObject* name_bytes)
+_type_lookup(PyTypeObject* tp, PyObject* name
+#ifndef PyObjC_FAST_UNICODE_ASCII
+		, PyObject* name_bytes
+#endif
+)
 {
 	Py_ssize_t i, n;
 	PyObject *mro, *base, *dict;
 	PyObject *descr = NULL;
 	PyObject* res;
+#ifndef PyObjC_FAST_UNICODE_ASCII
 	SEL	  sel = PyObjCSelector_DefaultSelector(PyBytes_AsString(name_bytes));
+#else
+	SEL	  sel = PyObjCSelector_DefaultSelector(PyObjC_Unicode_Fast_Bytes(name));
+#endif
 
 	/* TODO: if sel.startswith('__') and sel.endswith('__'): look_in_runtime = False */
 
 }
 
 static inline PyObject*
-_type_lookup_harder(PyTypeObject* tp, PyObject* name, PyObject* name_bytes)
+_type_lookup_harder(PyTypeObject* tp, PyObject* name
+#ifndef PyObjC_FAST_UNICODE_ASCII
+	, PyObject* name_bytes
+#endif 
+	)
 	/* See function of same name in objc-object.m for an explanation */
 {
 	Py_ssize_t i, n;
 						method_getName(m),
 						selbuf,
 						sizeof(selbuf));
-			if (strcmp(sel_name, PyBytes_AS_STRING(name_bytes)) == 0) {
+			if (strcmp(sel_name, 
+#ifndef PyObjC_FAST_UNICODE_ASCII
+					PyBytes_AS_STRING(name_bytes)
+#else
+					PyObjC_Unicode_Fast_Bytes(name)
+#endif
+					) == 0) {
 				/* Create (unbound) selector */
 				descr = PyObjCSelector_NewNative(
 						cls, method_getName(m), method_getTypeEncoding(m), 1);
 
 /* FIXME: version of _type_lookup that only looks for instance methods */
 static inline PyObject*
-_type_lookup_instance(PyObject* class_dict, PyTypeObject* tp, PyObject* name, PyObject* name_bytes)
+_type_lookup_instance(PyObject* class_dict, PyTypeObject* tp, PyObject* name
+#ifndef PyObjC_FAST_UNICODE_ASCII
+	, PyObject* name_bytes
+#endif
+	)
 {
 	Py_ssize_t i, n;
 	PyObject *mro, *base, *dict;
 	PyObject *descr = NULL;
 	PyObject* res;
+#ifndef PyObjC_FAST_UNICODE_ASCII
 	SEL	  sel = PyObjCSelector_DefaultSelector(PyBytes_AsString(name_bytes));
+#else
+	SEL	  sel = PyObjCSelector_DefaultSelector(PyObjC_Unicode_Fast_Bytes(name));
+#endif
 
 	/* TODO: if sel.startswith('__') and sel.endswith('__'): look_in_runtime = False */
 
 }
 
 static inline PyObject*
-_type_lookup_instance_harder(PyObject* class_dict, PyTypeObject* tp, PyObject* name, PyObject* name_bytes)
+_type_lookup_instance_harder(PyObject* class_dict, PyTypeObject* tp, PyObject* name
+#ifndef PyObjC_FAST_UNICODE_ASCII
+		, PyObject* name_bytes
+#endif
+	)
 {
 	Py_ssize_t i, n;
 	PyObject *mro, *base;
 	PyObject *descr = NULL;
 	PyObject* res;
+#ifndef PyObjC_FAST_UNICODE_ASCII
 	SEL	  sel = PyObjCSelector_DefaultSelector(PyBytes_AsString(name_bytes));
+#else
+	SEL	  sel = PyObjCSelector_DefaultSelector(PyObjC_Unicode_Fast_Bytes(name));
+#endif
 
 	/* Look in tp_dict of types in MRO */
 	mro = tp->tp_mro;
 						selbuf,
 						sizeof(selbuf));
 
-			if (strcmp(sel_name, PyBytes_AS_STRING(name_bytes)) == 0) {
+			if (strcmp(sel_name, 
+#ifndef PyObjC_FAST_UNICODE_ASCII
+					PyBytes_AS_STRING(name_bytes)
+#else
+					PyObjC_Unicode_Fast_Bytes(name)
+#endif
+					) == 0) {
 				/* Create (unbound) selector */
 				PyObject* result = PyObjCSelector_NewNative(
 						cls, sel, method_getTypeEncoding(m), 0);
 {
 	PyObject *descr = NULL;
 	PyObject *result = NULL;
-	PyObject *bytes;
+#ifndef PyObjC_FAST_UNICODE_ASCII
+	PyObject *name_bytes;
+#endif
 	descrgetfunc f;
 
 	/* Python will look for a number of "private" attributes during 
 			}
 			PyErr_Clear();
 		}
-
-	  	bytes = PyUnicode_AsEncodedString(name, NULL, NULL);
-		if (bytes == NULL) return NULL;
+#ifndef PyObjC_FAST_UNICODE_ASCII
+	  	name_bytes = PyUnicode_AsEncodedString(name, NULL, NULL);
+		if (name_bytes == NULL) return NULL;
+#else
+		if (PyObjC_Unicode_Fast_Bytes(name) == NULL) return NULL;
+#endif
 
 #if PY_MAJOR_VERSION == 2
 	} else if (PyString_Check(name)) {
 			PyErr_Clear();
 		}
 
-		bytes = name;
+		name_bytes = name;
 		Py_INCREF(bytes);
 #endif
 	} else {
 	}
 	PyObjCClass_CheckMethodList(self, 1);
 	
-	descr = _type_lookup(Py_TYPE(self), name, bytes);
+	descr = _type_lookup(Py_TYPE(self), name
+#ifndef PyObjC_FAST_UNICODE_ASCII
+		, name_bytes
+#endif
+	);
 
 	f = NULL;
 	if (descr != NULL 
 		}
 	}
 
-	if (strcmp(PyBytes_AS_STRING(bytes), "__dict__") == 0) {
+	if (strcmp(
+#ifndef PyObjC_FAST_UNICODE_ASCII
+		PyBytes_AS_STRING(name_bytes), 
+#else
+		PyObjC_Unicode_Fast_Bytes(name),
+#endif
+		"__dict__") == 0) {
+
 		result = ((PyTypeObject*)self)->tp_dict;
 		goto done;
 
 
 
 	if (descr == NULL) {
-		descr = _type_lookup_instance(((PyTypeObject*)self)->tp_dict, (PyTypeObject*)self, name, bytes);
+		descr = _type_lookup_instance(((PyTypeObject*)self)->tp_dict, (PyTypeObject*)self, name
+#ifndef PyObjC_FAST_UNICODE_ASCII
+			, name_bytes
+#endif
+		);
 		if (descr != NULL 
 #if PY_MAJOR_VERSION == 2
 			&& PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_HAVE_CLASS)
 		 *      is more correct, but more likely to be inefficient (but how often are
 		 *      instance methods accessed through the class anyway?
 		 */
-		descr = _type_lookup_harder(Py_TYPE(self), name, bytes);
+		descr = _type_lookup_harder(Py_TYPE(self), name
+#ifndef PyObjC_FAST_UNICODE_ASCII
+			, name_bytes
+#endif
+		);
 		if (descr != NULL 
 #if PY_MAJOR_VERSION == 2
 			&& PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_HAVE_CLASS)
 	}
 
 	if (descr == NULL) {
-		descr = _type_lookup_instance_harder(((PyTypeObject*)self)->tp_dict, (PyTypeObject*)self, name, bytes);
+		descr = _type_lookup_instance_harder(((PyTypeObject*)self)->tp_dict, (PyTypeObject*)self, name
+#ifndef PyObjC_FAST_UNICODE_ASCII
+			, name_bytes
+#endif
+		);
 		if (descr != NULL 
 #if PY_MAJOR_VERSION == 2
 			&& PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_HAVE_CLASS)
 
 	/* Try to find the method anyway */
 	PyErr_Clear();
-	if (PyObjCClass_HiddenSelector(self, sel_getUid(PyBytes_AsString(bytes)), YES)) {
-		Py_DECREF(bytes);
+	if (PyObjCClass_HiddenSelector(self, 
+#ifndef PyObjC_FAST_UNICODE_ASCII
+			sel_getUid(PyBytes_AsString(name_bytes)), 
+#else
+			sel_getUid(PyObjC_Unicode_Fast_Bytes(name)), 
+#endif
+			YES)) {
+#ifndef PyObjC_FAST_UNICODE_ASCII
+		Py_DECREF(name_bytes);
+#endif
 		PyErr_SetObject(PyExc_AttributeError, name);
 		return NULL;
 	}
-	result = PyObjCSelector_FindNative(self, PyBytes_AsString(bytes));
+	result = PyObjCSelector_FindNative(self, 
+#ifndef PyObjC_FAST_UNICODE_ASCII
+		PyBytes_AsString(name_bytes)
+#else
+		PyObjC_Unicode_Fast_Bytes(name)
+#endif
+	);
 
 	if (result != NULL) {
 		int res = PyDict_SetItem(((PyTypeObject*)self)->tp_dict, name, result);
 		}
 	}
 done:
-	Py_DECREF(bytes);
+#ifndef PyObjC_FAST_UNICODE_ASCII
+	Py_DECREF(name_bytes);
+#endif
 	return result;
 }
 

File pyobjc-core/Modules/objc/objc-object.m

 
 
 static inline PyObject*
-_type_lookup(PyTypeObject* tp, PyObject* name, PyObject* name_bytes)
+_type_lookup(PyTypeObject* tp, PyObject* name
+#ifndef PyObjC_FAST_UNICODE_ASCII
+		, PyObject* name_bytes
+#endif
+	)
 {
 	Py_ssize_t i, n;
 	PyObject *mro, *base, *dict;
 	PyObject *descr = NULL;
 	PyObject* res;
+#ifndef PyObjC_FAST_UNICODE_ASCII
 	SEL	  sel = PyObjCSelector_DefaultSelector(PyBytes_AsString(name_bytes));
+#else
+	SEL	  sel = PyObjCSelector_DefaultSelector(PyObjC_Unicode_Fast_Bytes(name));
+#endif
 
 	/* Look in tp_dict of types in MRO */
 	mro = tp->tp_mro;
 }
 
 static inline PyObject*
-_type_lookup_harder(PyTypeObject* tp, PyObject* name, PyObject* name_bytes)
+_type_lookup_harder(PyTypeObject* tp, PyObject* name
+#ifndef PyObjC_FAST_UNICODE_ASCII
+		, PyObject* name_bytes
+#endif
+	)
 	/* XXX: Name needs changing.
 	 *      Second pass through the class hierarchy when _type_lookup failed and the name is not in __dict__
 	 *      Used to look for selectors that cannot be found using the default translation from Python to ObjC
 						sizeof(selbuf));
 			if (sel_name == NULL) continue;
 
-			if (strcmp(sel_name, PyBytes_AS_STRING(name_bytes)) == 0) {
+			if (strcmp(sel_name, 
+#ifndef PyObjC_FAST_UNICODE_ASCII
+				PyBytes_AS_STRING(name_bytes)
+#else
+				PyObjC_Unicode_Fast_Bytes(name)
+#endif
+				) == 0) {
+
 				/* Create (unbound) selector */
 				descr = PyObjCSelector_NewNative(
 						cls, method_getName(m), 
 	PyObject *res = NULL;
 	descrgetfunc f;
 	PyObject** dictptr;
-	char* namestr;
 	id obj_inst;
-	PyObject* bytes;
+	const char* namestr;
+#ifndef PyObjC_FAST_UNICODE_ASCII
+	PyObject* name_bytes;
+#endif
 
 	if (name == NULL) {
 		PyErr_SetString(PyExc_TypeError, "<nil> name");
 	}
 
 	if (PyUnicode_Check(name)) {
-		bytes = PyUnicode_AsEncodedString(name, NULL, NULL);
-		if (bytes == NULL) return NULL;
+#ifndef PyObjC_FAST_UNICODE_ASCII
+		name_bytes = PyUnicode_AsEncodedString(name, NULL, NULL);
+		if (name_bytes == NULL) return NULL;
+#else
+		if (PyObjC_Unicode_Fast_Bytes(name) == NULL) return NULL;
+#endif
+
 #if PY_MAJOR_VERSION == 2
 	} else if (PyString_Check(name)) {
-		bytes = name; Py_INCREF(bytes);
+		name_bytes = name; Py_INCREF(name_bytes);
 #endif
 	} else {
 		PyErr_Format(PyExc_TypeError,
 
 
 
-	namestr = PyBytes_AsString(bytes);
+#ifndef PyObjC_FAST_UNICODE_ASCII
+	namestr = PyBytes_AsString(name_bytes);
+#else
+	namestr = PyObjC_Unicode_Fast_Bytes(name);
+#endif
 	if (namestr == NULL) {
 		if (!PyErr_Occurred()) {
 			PyErr_SetString(PyExc_ValueError, "Empty name");
 
 	/* replace _PyType_Lookup */
 	if (descr == NULL) {
-		descr = _type_lookup(tp, name, bytes);
+		descr = _type_lookup(tp, name
+#ifndef PyObjC_FAST_UNICODE_ASCII
+			, name_bytes
+#endif
+		);
 	}
 
 	f = NULL;
 		}
 	}
 
-	if (strcmp(PyBytes_AS_STRING(bytes), "__del__") == 0) {
+	if (strcmp(
+#ifndef PyObjC_FAST_UNICODE_ASCII
+		PyBytes_AS_STRING(name_bytes), 
+#else
+		PyObjC_Unicode_Fast_Bytes(name),
+#endif
+
+		"__del__") == 0) {
+
 		res = PyObjCClass_GetDelMethod((PyObject*)Py_TYPE(obj));
 		if (res != NULL) {
 			/* XXX: bind self */	
 	if (dictptr != NULL) {
 		PyObject *dict;
 
-		if (strcmp(PyBytes_AS_STRING(bytes), "__dict__") == 0) {
+		if (strcmp(
+#ifndef PyObjC_FAST_UNICODE_ASCII
+			PyBytes_AS_STRING(name_bytes), 
+#else
+			PyObjC_Unicode_Fast_Bytes(name),
+#endif
+			"__dict__") == 0) {
+
 			res = *dictptr;
 			if (res == NULL) {
 				*dictptr = PyDict_New();
 		 * for a method where the selector does not conform to the
 		 * naming convention that _type_lookup expects.
 		 */
-		descr = _type_lookup_harder(tp, name, bytes);
+		descr = _type_lookup_harder(tp, name
+#ifndef PyObjC_FAST_UNICODE_ASCII
+			, name_bytes
+#endif
+		);
 		if (descr != NULL 
 #if PY_MAJOR_VERSION == 2
 			&& PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_HAVE_CLASS)
 			res = NULL;
 		}
 	}
-	Py_DECREF(bytes);
+#ifndef PyObjC_FAST_UNICODE_ASCII
+	Py_DECREF(name_bytes);
+#endif
 	return res;
 }
 
 	int res;
 	id obj_inst;
 	NSString *obj_name;
-	PyObject* bytes;
+#ifndef PyObjC_FAST_UNICODE_ASCII
+	PyObject* name_bytes;
+#endif
 	
 	if (PyUnicode_Check(name)) {
-		bytes = PyUnicode_AsEncodedString(name, NULL, NULL);
-		if (bytes == NULL) return -1;
+#ifndef PyObjC_FAST_UNICODE_ASCII
+		name_bytes = PyUnicode_AsEncodedString(name, NULL, NULL);
+		if (name_bytes == NULL) return -1;
+#else
+		if (PyObjC_Unicode_Fast_Bytes(name) == NULL) return -1;
+#endif
+
 #if PY_MAJOR_VERSION == 2
 	} else if (PyString_Check(name)) {
-		bytes = name; Py_INCREF(bytes);
+		name_bytes = name; Py_INCREF(name_bytes);
 #endif
 	} else {
 		PyErr_Format(PyExc_TypeError,
 	if (obj_inst == nil) {
 		PyErr_Format(PyExc_AttributeError,
 		     "Cannot set '%s.400s' on NIL '%.50s' object",
-		     PyBytes_AS_STRING(bytes),
+#ifndef PyObjC_FAST_UNICODE_ASCII
+		     PyBytes_AS_STRING(name_bytes),
+#else
+		     PyObjC_Unicode_Fast_Bytes(name),
+#endif
 		     tp->tp_name);
-		Py_DECREF(bytes);
+#ifndef PyObjC_FAST_UNICODE_ASCII
+		Py_DECREF(name_bytes);
+#endif
 		return -1;
 	}
 
 	obj_name = nil;
 	if (((PyObjCClassObject*)tp)->useKVO) {
 		if ((PyObjCObject_GetFlags(obj) & PyObjCObject_kUNINITIALIZED) == 0) {
-			obj_name = [NSString stringWithUTF8String:PyBytes_AS_STRING(bytes)];
+			obj_name = [NSString stringWithUTF8String:
+#ifndef PyObjC_FAST_UNICODE_ASCII
+				PyBytes_AS_STRING(name_bytes)
+#else
+				PyObjC_Unicode_Fast_Bytes(name)
+#endif
+			];
 			_UseKVO((NSObject *)obj_inst, obj_name, YES);
 		}
 	}
-	descr = _type_lookup(tp, name, bytes);
+	descr = _type_lookup(tp, name
+#ifndef PyObjC_FAST_UNICODE_ASCII
+		, name_bytes
+#endif
+	);
 	f = NULL;
 	if (descr != NULL 
 #if PY_MAJOR_VERSION == 2
 	if (descr == NULL) {
 		PyErr_Format(PyExc_AttributeError,
 			     "'%.50s' object has no attribute '%.400s'",
-			     tp->tp_name, PyBytes_AS_STRING(bytes));
+			     tp->tp_name, 
+#ifndef PyObjC_FAST_UNICODE_ASCII
+			     PyBytes_AS_STRING(name_bytes)
+#else
+			     PyObjC_Unicode_Fast_Bytes(name)
+#endif
+		     );
 		res = -1;
 		goto done;
 	}
 
 	PyErr_Format(PyExc_AttributeError,
 		     "'%.50s' object attribute '%.400s' is read-only",
-		     tp->tp_name, PyBytes_AS_STRING(bytes));
+		     tp->tp_name, 
+#ifndef PyObjC_FAST_UNICODE_ASCII
+		     PyBytes_AS_STRING(name_bytes)
+#else
+		     PyObjC_Unicode_Fast_Bytes(name)
+#endif
+	);
 	res = -1;
   done:
 	if (obj_inst && obj_name) {
 		_UseKVO((NSObject *)obj_inst, obj_name, NO);
 	}
-	Py_DECREF(bytes);
+#ifndef PyObjC_FAST_UNICODE_ASCII
+	Py_DECREF(name_bytes);
+#endif
 	return res;
 }
 

File pyobjc-core/Modules/objc/pyobjc-compat.h

 #endif /* CGFLOAT_DEFINED */
 
 
+#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 3
+
+/* 
+ * A micro optimization: when using Python 3.3 or later it
+ * is possible to access a 'char*' with an ASCII representation
+ * of a unicode object without first converting it to a bytes
+ * string (if the string can be encoded as ASCII in the first
+ * place.
+ *
+ * This slightly reduces the object allocation rate during
+ * attribute access.
+ */
+
+#  define PyObjC_FAST_UNICODE_ASCII 1
+
+extern const char* PyObjC_Unicode_Fast_Bytes(PyObject* object);
+
+#endif
+
+
 #ifndef NSINTEGER_DEFINED
 
 #ifdef __LP64__

File pyobjc-core/Modules/objc/pyobjc-compat.m

 	}
 	return PyObjC_InternValue(r);
 }
+
+#ifdef PyObjC_FAST_UNICODE_ASCII
+
+const char* PyObjC_Unicode_Fast_Bytes(PyObject* object)
+{
+	if (!PyUnicode_Check(object)) {
+		PyErr_SetString(PyExc_UnicodeDecodeError, "Not a unicode object");
+		return NULL;
+	}
+	if (!PyUnicode_IS_ASCII(object)) {
+		PyErr_SetString(PyExc_UnicodeDecodeError, "Not an ASCII string");
+		return NULL;
+	}
+	return (const char*)(PyUnicode_DATA(object));
+}
+
+#endif /* PyObjC_FAST_UNICODE_ASCII */