Why can't categories have instance variables?
Asked Answered
S

1

18

I understand we can use associative references to invoke ivar-like behavior in categories. But what's the specific reason behind not being able to declare new ivars in categories?

Is it because we would invade the private space of the class? Or is there any other reason? If yes, I would appreciate an example that shows the ability to declare ivars in categories breaking whatever it breaks.

Spell answered 10/1, 2014 at 3:24 Comment(2)
If you want an example you can simply try it. Create a category and try to add an ivar and see what happens.Undistinguished
you can always simulate it with associated objectsInaugurate
U
25

Think of the Objective-C's ivars like a plain old C-structure. When you instantiate an instance of a class, a block of memory is created large enough to hold that structure.

Let's say you have an NSString. Lots and lots of existing code is compiled to use NSString. Lots of this code is built into libraries and frameworks. That compiled code was created knowing that the ivars of NSString take X number of bytes and are at some given offsets within that memory.

Now in your own little project lets say you create a category on NSString and want to add an ivar. In theory, any code in your project that includes the header file for the category would know that the size of this "new" NSString (plus category) takes X + Y bytes. This is much like a subclass. This newly compiled code could properly deal with the additional ivar(s).

But all of the pre-compiled code, the libraries and frameworks, would have no knowledge of the additional ivars. When NSString instances are created there, the memory is only X bytes, not X + Y bytes. Chaos ensues as your app code gets a reference to that smaller chunk of memory and tries to access the bytes for the category ivar. Things would go boom.

With a plain old subclass, things work because any code that can use the subclass' ivars knows about the subclass's ivars. But with a category, pre-existing code has no knowledge of the additions and won't properly create the space for them.

I suppose I should specify that all of the above is largely an educated guess. I could be totally wrong. It seems reasonable at least. :)

Undistinguished answered 10/1, 2014 at 4:2 Comment(7)
My understanding was that the modern (iOS and Intel 64bit) runtime uses dynamic instance variables, determining the size of classes at launch time rather than at build time, to avert the fragile base class problem — see cocoawithlove.com/2010/03/… . So I guess the problem now may be more about the dynamic linking sequence? I'm speculating wildly and quite possibly wrong or confused.Amasa
@Amasa That would theoretically be possible some of the runtime API but you wouldn't be able to benefit from direct references (_myvar). With your main implementation, the class can know the exact memory offset (plus base class size) to reference. The category would have to look it up and the dereference would be as costly as an associated object. At best it would save you a few bytes.Kaminsky
@Undistinguished Gotcha, in that case if I write a category for a custom view controller and declare a property for it, why does it still bomb out? In this case, doesn't the pre-compiled binary already know what X+Y is?Spell
Categories are attached at runtime. There is no difference between an Apple class and your custom view controller.Three
BTW, while the gist of the above is true (and it used to actually be true), the points about indirect allocation are also true since ObjC2. But it doesn't really help. Categories are attached at runtime, so you'd have to have a way to reach into existing objects and modify their storage layout (direct or indirect doesn't really matter). Such a thing could be possible, but not in the kind of "I'm really a thin layer on top of C, with a nice runtime" that is the heart of ObjC.Three
If a class and a category are linked into the same executable at build time then as an optimization the linker can usually attach the category to the class. But the compiler doesn't know in advance whether that trick will work.Mushroom
Not allowing categories to add storage was as much a design decision as an implementation detail. It was decided quite early that, like limiting dot syntax to strongly typed object references, not allowing categories to add storage, through properties or otherwise, was a net positive.Baty

© 2022 - 2024 — McMap. All rights reserved.