Pergunta

I want to get all the native classes (NSString, NSNumber, int, float, NSSet, NSDictionary) that I have loaded into my iOS Project..

i.e., if I have created a custom class named "TestClass" I don't want it listed...

I have already got a code but it returns names of all classes loaded any way I can modify the code to limit the list to Native classes only?

#import <objc/runtime.h>
#import <dlfcn.h>
#import <mach-o/ldsyms.h>


unsigned int count;
const char **classes;
Dl_info info;

dladdr(&_mh_execute_header, &info);
classes = objc_copyClassNamesForImage(info.dli_fname, &count);

for (int i = 0; i < count; i++) {
  NSLog(@"Class name: %s", classes[i]);
  Class class = NSClassFromString ([NSString stringWithCString:classes[i] encoding:NSUTF8StringEncoding]);
  // Do something with class

}
Foi útil?

Solução

You would get all loaded classes with

int numClasses;
Class * classes = NULL;

classes = NULL;
numClasses = objc_getClassList(NULL, 0);

if (numClasses > 0 )
{
    classes = (__unsafe_unretained Class *)malloc(sizeof(Class) * numClasses);
    numClasses = objc_getClassList(classes, numClasses);
    for (int i = 0; i < numClasses; i++) {
        Class c = classes[i];
        NSLog(@"%s", class_getName(c));
    }
    free(classes);
}

(Code from objc_getClassList documentation.)

To restrict the list, you can check the bundle from which the class was loaded, e.g.

Class c = classes[i];
NSBundle *b = [NSBundle bundleForClass:c];
if (b != [NSBundle mainBundle])
    ...

for all classes that are not loaded from your application.

Outras dicas

Here's a pure Swift solution with Swift 3:

var numClasses: Int32 = 0
var allClasses: AutoreleasingUnsafeMutablePointer<AnyClass?>? = nil
defer {
    allClasses = nil
}

numClasses = objc_getClassList(nil, 0)

if numClasses > 0 {
    var ptr = UnsafeMutablePointer<AnyClass?>.allocate(capacity: Int(numClasses))
    defer {
        ptr.deinitialize()
        ptr.deallocate(capacity: Int(numClasses))
    }
    allClasses = AutoreleasingUnsafeMutablePointer<AnyClass?>(ptr)
    numClasses = objc_getClassList(allClasses, numClasses)

    for i in 0 ..< numClasses {
        if let currentClass: AnyClass = allClasses?[Int(i)] {
            print("\(currentClass)")
        }
    }
}

Original solution with Swift 2.2/Xcode 7.3:

var numClasses: Int32 = 0
var allClasses: AutoreleasingUnsafeMutablePointer<AnyClass?> = nil
defer {
    allClasses = nil
}

numClasses = objc_getClassList(nil, 0)

if numClasses > 0 {
    var ptr = UnsafeMutablePointer<AnyClass>.alloc(Int(numClasses))
    defer {
        ptr.destroy()
        ptr.dealloc(Int(numClasses))
        ptr = nil
    }
    allClasses = AutoreleasingUnsafeMutablePointer<AnyClass?>.init(ptr)
    numClasses = objc_getClassList(allClasses, numClasses)

    for i in 0 ..< numClasses {
        if let currentClass: AnyClass = allClasses[Int(i)] {
            print("\(currentClass)")
        }
    }
}

Note that due to the way Swift handles weak pointers (protip: it doesn't), your classes will be overreleased with this code. I've opened SR-1068 about bridging __weak and __unsafe_unretained pointers to Swift. __weak pointers are bridged as UnsafeMutablePointer while __unsafe_unretained pointers are bridged as AutoreleasingUnsafeMutablePointer, which causes the overrelase.

Fortunately, Classes don't do anything on release, so this code is relatively safe, at least for now.

Using Runtime it is possible to:

Objective-C

#import <objc/runtime.h>

- (void) printClassNames {
    int amountClasses = objc_getClassList(NULL, 0);
    printf("Amount of classes: %d", amountClasses);

    Class *classes = (__unsafe_unretained Class *)malloc(sizeof(Class) * amountClasses);
    amountClasses = objc_getClassList(classes, amountClasses);

    for (int i = 0; i < amountClasses; i++) {
        Class class = classes[i];

        if ([NSBundle bundleForClass:class] != [NSBundle mainBundle]) { // restriction that pass classes from main bundle
            continue;
        }

        printf("Class name: %s", class_getName(class));

        [self printPropertyNamesForClass:class];
        [self printMethodNamesForClass:class];

    }

    free(classes);
}

- (void) printPropertyNamesForClass:(Class) class {
    uint count;
    objc_property_t* properties = class_copyPropertyList(class, &count);

    for (int i = 0; i < count ; i++) {

        const char* propertyName = property_getName(properties[i]);
        printf("\t Property name: %s \n", propertyName);
    }
    free(properties);
}

- (void) printMethodNamesForClass:(Class) class {
    //List of all methods
    unsigned int amountMethod = 0;
    Method *methods = class_copyMethodList(class, &amountMethod);

    for (unsigned int i = 0; i < amountMethod; i++) {
        Method method = methods[i];

        printf("\t method named:'%s' \n", sel_getName(method_getName(method)));
    }

    free(methods);
}

Swift

func printClassNames() {

    let amountClasses = objc_getClassList(nil, 0)
    print("Amount of classes: \(amountClasses)")

    var classes = [AnyClass](repeating: NSObject.self, count: Int(amountClasses))
    classes.withUnsafeMutableBufferPointer { buffer in
        let autoreleasingPointer = AutoreleasingUnsafeMutablePointer<AnyClass>(buffer.baseAddress)
        objc_getClassList(autoreleasingPointer, amountClasses)
    }

    for currentClass in classes {

        guard Bundle(for: currentClass) == Bundle.main else {continue}
        print("Class name:\(currentClass)")

        printPropertyNamesForClass(currentClass)
        printMethodNamesForClass(currentClass)
    }

}

func printPropertyNamesForClass(_ currentClass : AnyClass) {
    var count = UInt32()
    let propertyList = class_copyPropertyList(currentClass, &count)
    let intCount = Int(count)

    guard let properties = propertyList, intCount > 0 else {return}

    for i in 0 ..< intCount {
        let property : objc_property_t = properties[i]

        let nameCString = property_getName(property)
        print("\t Property name:\(String(cString: nameCString))");

    }

    free(properties)
}

func printMethodNamesForClass(_ currentClass: AnyClass) {
    var methodCount: UInt32 = 0
    let methodList = class_copyMethodList(currentClass, &methodCount)

    guard let methods = methodList, methodCount > 0 else {return}

    var ptr = methods
    for _ in 0 ..< methodCount {

        let sel = method_getName(ptr.pointee)
        ptr = ptr.successor()

        let nameCString = sel_getName(sel)

        print("\t method named:\(String(cString: nameCString))");
    }

}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top