Get all subclasses of UIView in runtime in Objective-c - objective-c

Is is possible in Objective-c runtime to get an array of all subclasses of UIView class?

Here's the code that prints names of all UIView subclasses: (How to get list of all classes available at runtime can be found here). Note also that this code will print only direct UIView subclasses, if you want to go further down the hierarchy you'll probably need to call listed code recursively with desired superclass as a parameter.
Class * classes = NULL;
int numClasses = objc_getClassList(NULL, 0);
if (numClasses > 0 )
{
classes = malloc(sizeof(Class) * numClasses);
numClasses = objc_getClassList(classes, numClasses);
for (int i = 0; i < numClasses; ++i){
if (class_getSuperclass(classes[i]) == [UIView class]){
NSLog(#"%#", NSStringFromClass(classes[i]));
}
}
free(classes);
}

Related

Crash when processing __NSAtom class object

Hi I'm using this answer to get list of class to automatically build some structure.
My code looks like that:
NSMutableDictionary *result = [NSMutableDictionary dictionaryWithCapacity: 32];
Class* classes = NULL;
int numClasses = objc_getClassList(NULL, 0);
if (numClasses > 0 ) {
classes = (Class*)malloc(sizeof(Class) * numClasses);
numClasses = objc_getClassList(classes, numClasses);
for (int index = 0; index < numClasses; index++) {
Class nextClass = classes[index];
if ([nextClass isSubclassOfClass: BaseCmd.class] &&
![BaseCmd isSubclassOfClass: nextClass]) {
BaseCmd *cmd = [nextClass new];
result[cmd.name] = cmd;
}
}
free(classes);
}
return result;
So modification from answer is quite simple. It does work quite nicely until __NSAtom class is processed (index = 1548 numClasses = 7628 so it is not out of bounds problem).
When this __NSAtom class is reached there is a crash. Logs show:
*** NSForwarding: warning: object 0x7fff74978938 of class '__NSAtom' does not implement methodSignatureForSelector: -- trouble ahead
*** NSForwarding: warning: object 0x7fff74978938 of class '__NSAtom' does not implement doesNotRecognizeSelector: -- abort
Crash comes from condition isSubclassOfClass.
Why it doesn't work? How can I fix it?
I'm using OS X Yosemite.
+[NSObject isSubclassOfClass:] is a class method for NSObject and not all classes are subclasses of NSObject.
It seems as if you have find private class that is not a subclass of NSObject, so it requires a more delicate handling for checking for inheritance. Try:
BOOL isSubclass(Class child, Class parent) {
for (Class c = child; c != Nil; c = class_getSuperclass(c)) {
if (c == parent) {
return YES;
}
}
return NO;
}

Why does my superclass object call its subclass method?

I have seen so many helpful threads here, but this is my first time posting!
I was working on the infamous Stanford OpenCourse project: Matchismo. While I got everything going just fine, but I don't understand one part of the sample codes.
Basically the code below is used to get a Card object to compare with another card.
- (void) flipCardAtIndex: (NSUInteger)index
{
Card *card = [self cardAtIndex:index];
if (card && !card.isUnplayable)
{
if (!card.isFaceUp)
{
for (Card* otherCard in self.cards)//for-in loop
{
if (otherCard.isFaceUp && !otherCard.isUnplayable)
{
int matchScore = [card match:#[otherCard]];
......
And this is how cardAtIndex works:
-(Card *) cardAtIndex:(NSUInteger)index
{
if (index < [self.cards count])
//dot notation is used for property
//[] is used for method
{
return self.cards[index];
}
return nil;
}
Here are the methods for Match(card*) and Match(playingCard)
Match(card*)
-(int) match:(NSArray *)otherCards
{
NSLog(#"here");
int score = 0;
for (Card *card in otherCards)
{
if ([card.content isEqualToString:self.content])
score = 1;
{
NSLog(#"Card Match");
}
}
return score;
}
Match(PlayingCard*)
-(int) match: (NSArray *)otherCards;
{
int score = 0;
if ([otherCards count] == 1)
{
PlayingCard *otherCard = [otherCards lastObject];//the last object in the array
if ([otherCard.suit isEqualToString:self.suit])
score = 1;
else if (otherCard.rank == self.rank)
score = 4;
NSLog(#"PlayingCard Match");
}
return score;
}
It worked just fine, but I don't get why when a Card* object calls a method, its subclass's PlayingCard's method is invoked.
Thanks so much for help me!
This concept is called Polymorphism.
It allows you to have a base class which provides some interface, and a set of subclasses that implement these methods in some different ways. The classic example is a Drawable class method draw, and its subclasses Circle and Rectangle, that both override the draw method to render themselves in some specific manner.
So goes for your Card base class, it calls its own interface method match, but as an object is actually not an instance of Card, but of a PlayingCard subclass, subclass method gets called instead to provide specific implementation.
In your view controller .m file, the property "deck" must be initialized as class PlayingCardDeck, and in PlayingCardDeck.m, the class of card is PalyingCard. So even though you declared your card as class Card, the method it calls will still be the one in class PlayingCard.

How do I get list of all classes conform to certain protocol in Xcode 4?

I am new to Xcode. I want to know how can I know all classes conform to certain protocol in Xcode 4.3.1?
And how can I know all subclasses of one class?
Use the Objective-C runtime functions.
objc_getClassList to get the list of Classes
class_getSuperclass or the -superclass method to walk the superclass chain
class_conformsToProtocol or the -conformsToProtocol: method to check if a class conforms to a protocol
Protocol *protocol = #protocol(YourProtocol);
int numberOfClasses = objc_getClassList(NULL, 0);
Class *classList = malloc(numberOfClasses * sizeof(Class));
numberOfClasses = objc_getClassList(classList, numberOfClasses);
for (int idx = 0; idx < numberOfClasses; idx++)
{
Class class = classList[idx];
if (class_getClassMethod(class, #selector(conformsToProtocol:)) && [class conformsToProtocol:protocol])
{
NSLog(#"%#", NSStringFromClass(class));
}
}
free(classList);

Why can some methods (-retainWeakReference, -allowsWeakReference, +load, +initialize) on class NSObject not be added to other classes at runtime?

It is straightforward at runtime to create a copy MyNSObject of the Class NSObject:
First, create a new class pair.
Class MyNSObject = objc_allocateClassPair(nil, "MyNSObject", 0);
Second read the methods, protocols, and ivars from NSObject and add them to the new class.
uint instanceMethodCount;
Method *instanceMethodArray = class_copyMethodList([NSObject class], &instanceMethodCount);
for (int i = 0; i < instanceMethodCount; i++) {
Method method = *(instanceMethodArray + i);
SEL selector = method_getName(method);
IMP implementation = method_getImplementation(method);
const char *types = method_getTypeEncoding(method);
BOOL success = class_addMethod(MyNSObject, selector, implementation, types);
}
free(instanceMethodArray);
uint protocolCount;
Protocol **protocolArray = class_copyProtocolList([NSObject class], &protocolCount);
for (int i = 0; i < protocolCount; i++) {
Protocol *protocol = *(protocolArray + i);
BOOL success = class_addProtocol(MyNSObject, protocol);
}
free(protocolArray);
uint ivarCount;
Ivar *ivarArray = class_copyIvarList([NSObject class], &ivarCount);
for (int i = 0; i < ivarCount; i++) {
Ivar ivar = *(ivarArray + i);
const char *name = ivar_getName(ivar);
const char *typeEncoding = ivar_getTypeEncoding(ivar);
NSUInteger size, alignment;
NSGetSizeAndAlignment(typeEncoding, &size, &alignment);
BOOL success = class_addIvar(MyNSObject, name, size, alignment, typeEncoding);
}
free (ivarArray);
Third, read the methods from the metaclass of NSObject and add them to the new metaclass.
uint classMethodCount;
Method *classMethodArray = class_copyMethodList(object_getClass([NSObject class]), &classMethodCount);
for (int i = 0; i < classMethodCount; i++) {
Method method = *(classMethodArray + i);
SEL selector = method_getName(method);
IMP implementation = method_getImplementation(method);
const char *types = method_getTypeEncoding(method);
BOOL success = class_addMethod(object_getClass(MyNSObject), selector, implementation, types);
}
free(classMethodArray);
And finally, register the class pair.
objc_registerClassPair(MyNSObject);
Well, it's almost that straightforward. There are a couple of problems with this. Well, a couple of couples. If we were to add the following lines at the end but within the first for block
if (!success) {
NSLog(#"unable to add method with selector named %# to class MyNSObject", NSStringFromSelector(selector));
}
and the following lines at the end but within the last for block
if (!success) {
NSLog(#"unable to add method with selector name %# to metaclass MyNSObject", NSStringFromSelector(selector));
}
Then we would see the following output:
unable to add method with selector name retainWeakReference to class MyNSObject
unable to add method with selector name allowsWeakReference to class MyNSObject
unable to add method with selector name load to metaclass MyNSObject
unable to add method with selector name initialize to metaclass MyNSObject
What is going on here? Do classes (resp. metaclasses) implement retainWeakReference and allowsWeakReferenc (resp. load and initialize) "out of the box"?
References:
1. Cocoa with Love - What is a meta-class in Objective-C?
2. Stack Overflow - Justin Spahr-Summers response to "How can one obtain the sizeof a type for which one has an encoding?"
NSObject is an even more interesting beast than expected. Typically one thinks about the map
method_getName: Method -> SEL
as being one-to-one. I.e one usually thinks that method_getName(methodA) == method_getName(methodB) just in case methodA == methodB. One is encouraged to think this: one cannot create a class during coding via #interface which has multiple methods with the same selector, nor can one add two methods with the same selector to a class using class_addMethod() during runtime.
However, it is evidently possible to do it by hand. The following code demonstrates this. This code gets all the instance methods on NSObject and prints out each one named either "retainWeakReference" or "allowsWeakReference" and then gets all the class methods on NSObject and prints out each one named either "initialize" or "load".
uint NSObjectInstanceMethodCount;
Method *NSObjectInstanceMethodArray = class_copyMethodList([NSObject class], &NSObjectInstanceMethodCount);
for (int i = 0; i < NSObjectInstanceMethodCount; i++) {
Method method = *(NSObjectInstanceMethodArray + i);
SEL selector = method_getName(method);
IMP implementation = method_getImplementation(method);
const char *types = method_getTypeEncoding(method);
if (strcmp(selector, "retainWeakReference") == 0 || strcmp(selector, "allowsWeakReference") == 0) {
NSLog(#"NSObject implements method(%s,%p,%s)", selector, implementation, types);
}
}
uint NSObjectClassMethodCount;
Method *NSObjectClassMethodArray = class_copyMethodList(object_getClass([NSObject class]), &NSObjectClassMethodCount);
for (int i = 0; i < NSObjectClassMethodCount; i++) {
Method method = *(NSObjectClassMethodArray + i);
SEL selector = method_getName(method);
IMP implementation = method_getImplementation(method);
const char *types = method_getTypeEncoding(method);
if (strcmp(selector, "initialize") == 0 || strcmp(selector, "load") == 0) {
NSLog(#"metaNSObject implements method(%s,%p,%s)", selector, implementation, types);
}
}
The output is not what one might, aside from the preceding build-up, have expected:
NSObject implements method(retainWeakReference,0x7fff8a120b1f,c16#0:8)
NSObject implements method(allowsWeakReference,0x7fff8a120b05,c16#0:8)
NSObject implements method(retainWeakReference,0x7fff80ad6db0,c16#0:8)
NSObject implements method(allowsWeakReference,0x7fff80ad6d90,c16#0:8)
metaNSObject implements method(load,0x7fff8a09e4f2,v16#0:8)
metaNSObject implements method(initialize,0x7fff8a00cb89,v16#0:8)
metaNSObject implements method(load,0x7fff80a57670,v16#0:8)
metaNSObject implements method(initialize,0x7fff80a133d0,v16#0:8)
So, as is now evident, NSObject has two implementations for each of the selectors -retainWeakReference, -allowsWeakReference, +load, and +initialize. These are the only four methods on NSObject for which there are multiple implementations, which is demonstrated by the fact that these were the only four reported by the code in the question as being unable to be added to MyNSObject.
A statement which gets close to counting as an answer to the question then is that you can't add multiple methods with the same selector to a class created at runtime via class_addMethod(). In particular, though, no, no methods are implemented by a class created at runtime with objc_allocateClassPair() "out of the box".

How can I find a class which inherit from a particular class from my Class list?

I get the list of all classes of my application.
I want to find each Class from my list which inherits from MySpecificWrapperClass.
int numClasses = objc_getClassList(NULL, 0);
Class *classes = malloc(sizeof(Class) * numClasses);
objc_getClassList(classes, numClasses);
for (int i = 0; i < numClasses; i++) {
Class class = classes[i];
if( isThisClassInheritsFrom(class,MySpecificWrapperClass )){
MySpecificWrapperClass o = [[class alloc]init];
...
}
}
free(classes);
Dose somebody know how to do that?
Thank for help.
Fred
There may be a better solution, but brute force should do it. class_getSuperclass returns the Class object for a given Class object's superclass. When you get to the root class, it returns Nil.
for (int i = 0; i < numClasses; i++) {
Class class = classes[i];
Class superclass = class_getSuperclass(class);
while( superclass != Nil ){
if( superclass == MySpecificWrapperClass ){
// Got one!
break;
}
superclass = class_getSuperclass(superclass);
};
}
This is assuming that you want to check the entire hierarchy for a given class, i.e., you want every class that has your class anywhere in its ancestors. If you only care about direct subclasses, obviously, you only need to call class_getSuperclass once.
To test if a class is inherits of your class, try
if ([obj isKindOfClass: MySpecificWrapperClass])
You can check if a class is a subclass of another class with the isSubclassOfClass: method defined by NSObject.
NSObject class reference