aboutsummaryrefslogtreecommitdiffhomepage
path: root/objectivec/GPBRootObject.m
diff options
context:
space:
mode:
Diffstat (limited to 'objectivec/GPBRootObject.m')
-rw-r--r--objectivec/GPBRootObject.m29
1 files changed, 22 insertions, 7 deletions
diff --git a/objectivec/GPBRootObject.m b/objectivec/GPBRootObject.m
index 4570716f..bad2f9a7 100644
--- a/objectivec/GPBRootObject.m
+++ b/objectivec/GPBRootObject.m
@@ -43,6 +43,14 @@
- (const char *)singletonNameC;
@end
+// We need some object to conform to the MessageSignatureProtocol to make sure
+// the selectors in it are recorded in our Objective C runtime information.
+// GPBMessage is arguably the more "obvious" choice, but given that all messages
+// inherit from GPBMessage, conflicts seem likely, so we are using GPBRootObject
+// instead.
+@interface GPBRootObject () <GPBMessageSignatureProtocol>
+@end
+
@implementation GPBRootObject
// Taken from http://www.burtleburtle.net/bob/hash/doobs.html
@@ -184,11 +192,10 @@ static id ExtensionForName(id self, SEL _cmd) {
dispatch_semaphore_wait(gExtensionSingletonDictionarySemaphore,
DISPATCH_TIME_FOREVER);
id extension = (id)CFDictionaryGetValue(gExtensionSingletonDictionary, key);
- if (extension) {
- // The method is getting wired in to the class, so no need to keep it in
- // the dictionary.
- CFDictionaryRemoveValue(gExtensionSingletonDictionary, key);
- }
+ // We can't remove the key from the dictionary here (as an optimization),
+ // two threads could have gone into +resolveClassMethod: for the same method,
+ // and ended up here; there's no way to ensure both return YES without letting
+ // both try to wire in the method.
dispatch_semaphore_signal(gExtensionSingletonDictionarySemaphore);
return extension;
}
@@ -212,9 +219,17 @@ BOOL GPBResolveExtensionClassMethod(Class self, SEL sel) {
#pragma unused(obj)
return extension;
});
- if (class_addMethod(metaClass, sel, imp, encoding)) {
- return YES;
+ BOOL methodAdded = class_addMethod(metaClass, sel, imp, encoding);
+ // class_addMethod() is documented as also failing if the method was already
+ // added; so we check if the method is already there and return success so
+ // the method dispatch will still happen. Why would it already be added?
+ // Two threads could cause the same method to be bound at the same time,
+ // but only one will actually bind it; the other still needs to return true
+ // so things will dispatch.
+ if (!methodAdded) {
+ methodAdded = GPBClassHasSel(metaClass, sel);
}
+ return methodAdded;
}
return NO;
}