5

How safe is the use of Class with Objective-C?

-> can i store the Class safe in a dictionary and than compare

info[@"class"] = [User class];
...
if ([User class] == info[@"class"]) {

}

-> can the class pointer change?

-> is it quarantined to be never Nil?

l0gg3r
  • 8,864
  • 3
  • 26
  • 46
Peter Lapisu
  • 19,915
  • 16
  • 123
  • 179
  • 1
    you are able to store it; it won't be updated in runtime by any Apple's framework; it will be never `nil`. – holex Nov 19 '14 at 16:06
  • @holex I will not agree with you, see my answer – l0gg3r Nov 19 '14 at 16:08
  • @l0gg3r, your answer is a bit messy. :( – holex Nov 19 '14 at 16:09
  • @holex basically you can override `+ (Class)class` method in you Class implementation, and return `Nil` there. – l0gg3r Nov 19 '14 at 16:12
  • @l0gg3r, indeed, the developer can do silly and pointless things anytime – that is the beautiful part of developing. :) but luckily Apple won't make such unpredictable madness – and the 3rd party solutions are always huge risks. – holex Nov 19 '14 at 16:14
  • :) sure, but Apple provided methods can be swizzled to, so this means you can swizzle +class method of any Class, and returns something else. – l0gg3r Nov 19 '14 at 16:17

2 Answers2

4

Class objects behave like normal objects. They can be retained, released, passed as arguments and return values, stored in ivars and properties, stored in containers — basically, anything.

[SomeClassName class] normally will not compile or link if such class can not be found, but it is possible for it to compile but return nil, for example, when running on OS which does not have that class available, i.e. older OS version than version of your development SDK. The return value of NSClassFromString will be nil, if such class does not exist.

Pointer value (identity) of class objects never changes. There is only one class object for each class name, and you can use C == operator to test if class pointers are the same class. (Subclass/superclass relationship can be tested using + isSubclassOfClass: class method).

Class objects are never deallocated — you can rely on them to be alive (i.e. without retaining them) until the process completely terminates.

The above is true for most applications; however, there is a tricky case of bundle loading (and even more tricky case of bundle unloading):

  • Loading bundle may add classes to the runtime, e.g. causing NSClassFromString to start returning non-nil for their names.
  • If dynamically loading a bundle causes class names to clash, the runtime currently logs a complaint but keeps working; it is not specified what exactly happens in that case.
  • Since Mac OS X 10.5, it is possible to unload a bundle, which causes its classes to be removed. It is not specified what should happen if some of those classes have been retained.
hamstergene
  • 24,039
  • 5
  • 57
  • 72
  • I think the poster would also like an explicit comment on comparison by identity. Which I think is safe given the semantics (subject to the bundle unloading caveat you raise). Would you agree? – Tommy Nov 19 '14 at 15:49
  • @Tommy True — I've added explicit statement about that. – hamstergene Nov 19 '14 at 16:00
  • hm, http://stackoverflow.com/questions/5948910/whats-the-difference-between-nil-and-nil – l0gg3r Nov 19 '14 at 16:11
  • [SomeClassName class] are always non-nil (otherwise, the binary will not compile, link and/or load). I'll not agree, you can try my provided example, and it will compile link and run, and also will not crash – l0gg3r Nov 19 '14 at 16:24
  • @l0gg3r As far as I know, `Nil` is not used, including Apple's own sample code and documentation. E.g. docs on `NSClassFromString` have been using lowercase `nil` as long as I remember. – hamstergene Nov 19 '14 at 16:26
  • 2
    "Expressions like [SomeClassName class] are always non-nil" Not true. If `SomeClassName` is a class only available on a newer OS version which is the base SDK of your app, and your deployment target is an older OS version, it will compile and link but when running on the old OS version, `[SomeClassName class]` will be `nil`. – newacct Nov 19 '14 at 20:12
2

Of course [User class] can be Nil.
+ class is a static method defined in NSObject class, so this means everyone can override + class and return any value.

Here is example how [User class] can return Nil

@implementation User

+ (Class)class
{
    return Nil;
}

@end

Also [User class] can return any value, and pointer can be changed

@implementation User

+ (Class)class
{
    switch (arc4random() % 3) {
        case 0:
            return [UIButton class];
            break;
        case 1:
            return [UILabel class];
            break;
        case 2:
            return [NSString class];
            break;
        default:
            break;
    }
    return Nil;
}

@end

Apple provided classes will always work as expected (like @hamstergene suggests) BUT ONLY if their +class method is not swizzled by programmer.

So using [User class] is not a safe way.
Instead of that, get classes directly from runtime using runtime function

#import <objc/runtime.h>

info[@"class"] = objc_getClass("User")
l0gg3r
  • 8,864
  • 3
  • 26
  • 46
  • ... but I guess the poster could call `object_getClass` if he wanted something that can't be overridden (without substantially breaking the runtime, anyway). – Tommy Nov 19 '14 at 15:51
  • @Tommy ah no, object_getClass returns the Class of object, but he uses directly `[User class]`, so there is no object. I think he should use `objc_getClass("User")` – l0gg3r Nov 19 '14 at 15:59
  • He definitely shouldn't use the `isa` pointer as it's deprecated in the 64-bit runtime to allow for tagged pointers. But you're right about it being `+class`, not `-class` that he's using and I was wrong. If he were to hit the runtime he'd need to lookup by string name, I guess. – Tommy Nov 19 '14 at 16:03
  • 2
    It's hard to think of a reason that anyone would ever override `+class` to return `nil`. Doing so would be very unusual. – Caleb Nov 19 '14 at 16:28
  • @WilliamShakespeare I got a project, where `[NSNull null]` is swizzled, and they are returning custom `NSProxy` which will not crash on any method calls. so that class also overrides `+class`, and returns `NSNull`. – l0gg3r Nov 19 '14 at 16:31
  • @l0gg3r Okay, but that's quite different from returning `nil`. It's the difference between pretending to be some other class and pretending there is no class at all. (Aside: you might want to check that `[[NSNull null] class]` returns `NSNull` too. It'd be easy to forget to override the instance method `-class`.) – Caleb Nov 19 '14 at 16:57
  • @WilliamShakespeare Yes, its hard, but you can't say that it is safe. the question actually says 'is it safe?' – l0gg3r Nov 19 '14 at 16:58