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.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Ravi
  • 7,929
  • 6
  • 38
  • 48

1 Answers1

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. :)

rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • 1
    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 http://www.cocoawithlove.com/2010/03/dynamic-ivars-solving-fragile-base.html . So I guess the problem now may be more about the dynamic linking sequence? I'm speculating wildly and quite possibly wrong or confused. – Tommy Jan 10 '14 at 04:11
  • @Tommy 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. – Brian Nickel Jan 10 '14 at 04:53
  • @rmaddy 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? – Ravi Jan 10 '14 at 05:27
  • 1
    Categories are attached at runtime. There is no difference between an Apple class and your custom view controller. – Rob Napier Jan 10 '14 at 05:28
  • 5
    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. – Rob Napier Jan 10 '14 at 05:30
  • 1
    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. – Greg Parker Jan 10 '14 at 10:11
  • 3
    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. – bbum May 23 '14 at 16:21