3

I am trying to create a structure that has several different variable of different types in it.

several of the types are of NSString, but trying to do this was causing an error

ARC forbids Objective-C objects in structs or unions

so having read about the error I see its sensible to add

__unsafe_unretained

before the NSString declaration, However I have no idea what the ramifications of this will be, I have had a quick read around and found this detailed post about the differences of

  • __strong
  • __weak
  • __unsafe_unretained

however it was still abit vague about whats going on with a NSString thats in a struct with __unsafe_unretained infront of it and was hoping someone can tell me whats going on and what I need to think about in the future regarding memory and stopping any leaks.

any help would be appreciated.

Community
  • 1
  • 1
HurkNburkS
  • 5,492
  • 19
  • 100
  • 183
  • possible duplicate of [ARC - The meaning of __unsafe_unretained?](http://stackoverflow.com/questions/8592289/arc-the-meaning-of-unsafe-unretained) – CodaFi Aug 08 '12 at 03:14

4 Answers4

10

Suppose, under ARC, you could declare a struct like this:

typedef struct {
    __strong NSObject *someObject;
    int someInteger;
} MyStruct;

Then you might write code like this:

MyStruct *thing = malloc(sizeof(MyStruct));

Problem: malloc doesn't zero-fill the memory it returns. So thing->someObject is some random value - not necessarily NULL. Then you assign a value to it like this:

thing->someObject = [[NSObject alloc] init];

Under the covers, ARC will turn that into code like this:

NSObject *temporary = [[NSObject alloc] init];
[thing->someObject release];
thing->someObject = temporary;

The problem here is that your program just sent release to some random value! Your app will probably crash at this point.

You might say that ARC should recognize the call to malloc and take care of setting someObject to NULL to prevent this. The problem is that malloc might be wrapped in some other function, like this:

void *myAllocate(size_t size) {
    void *p = malloc(size);
    if (!p) {
        // malloc failed.  Try to free up some memory.
        clearCaches();
        p = malloc(size);
    }
    return p;
}

OK, now ARC has to know about your myAllocate function too... and that might be inside some static library that you got as a binary.

Your app might even have its own memory allocators that recycle old allocations without using free and malloc every time. So even changing malloc to zero-fill the memory before returning it would not work. ARC would have to know about any custom allocators in your program.

It would be very, very hard to make this work reliably. So instead, the creators of ARC just gave up and said “Forget it. We're not going to let you put __strong and __weak references in structs.”

That's why you can only put an object pointer into a struct if you use __unsafe_unretained to tell ARC “Don't try to manage ownership of the object that this references.”

You can try to use a struct containing __unsafe_unretained object references, perhaps using CFRetain and CFRelease to manually retain and release them. Then you can create an array of such structs. This is error-prone, so you should only do it if the profiler tells you that it's critical for performance.

Instead, just create a new Objective-C class instead of a struct. Give the class an @property for each field you would have put in the struct. The use an NSMutableArray to manage an array of instances of this new class.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • Well, ARC is actually built into the language, whereas malloc is just a lowly library function. Who's breaking whom? ;-> – Jody Hagins Aug 08 '12 at 04:42
  • @JodyHagins I worry about that question a little too much... And besides, if malloc fails, the general response is to quit, run and hide in a corner until that wormhole you just created goes away. – CodaFi Aug 08 '12 at 04:52
  • lol... cool, thanks for your response, I understand my mistake now. the funny thing is I actually created a new Object class and tried to declare a struct in it.. lol I know what I need to do I loose it with the execution.. which I am trying to figure out by reading lots then presenting issues I am stuck with to helpfull people such as yourself on stakoverflow. – HurkNburkS Aug 08 '12 at 04:53
  • You can actually just use `-retain` & `-release` on an `__unsafe_unretained` pointer, it's one of the weird quirks of ARC. – Richard J. Ross III Aug 08 '12 at 11:38
  • 1
    Ownership qualifiers are for the pointers not the types, so use it that way `NSString * __string aString`. Apple allows the other way but advises **not** to write it that way, because it is simply wrong. – Binarian Jan 05 '14 at 13:07
4

Just more food for thought...

If you really need ARC pointers in non-objective-c code, you can use them natively in C++.

In fact, you can store ARC pointers in all the standard template containers, and they still retain (ha ha) their proper semantics.

struct Foo {
    NSDictionary *dictionary;
    NSString *string;
    __weak UIViewController *viewController;
};

std::list<UIView*> views;

Since the compiler treats ARC pointers as objects with non-trivial constructors, they can have their normal semantics.

All their ARC glory will be magically handled.

Jody Hagins
  • 27,943
  • 6
  • 58
  • 87
  • ha ha indeed! :P rhank you very much :) but I think for my own sake I should stick with Objective C class objects. however thank you for helping with my understanding of the situation.. I am still reading through your guys comments looking up the meaning of some of the tearms you are using I am unfamiliar with. – HurkNburkS Aug 08 '12 at 05:02
2

ARC just loves to complain! Actually, we have to look at this historically. In the old-style Manual Reference Counted environment from way back, the compiler didn't complain because it knew everything memory related was going to be your job, and your job alone. But that changed when Apple introduced Automatic Reference Counting, because the compiler needed to get substantially more anal about what type an object was and what it was contained in, so that it knew how to properly manage the memory for said object efficiently. When you place an Objective-C object into a C-struct, you're sort of sticking out your tongue at the compiler because a struct implies that you will own and manage the memory of the items inside of it yourself (that and ARC doesn't touch malloc and free). That's where __unsafe_unretained comes in. With it, we tell the compiler that any and all memory operations will be your responsibility, just like in MRC. ARC literally "can't guarantee the safety" of the object's pointer being nil after deallocation, so it makes you explicitly declare it as such.

If you want to avoid all of this nonsense, just make your struct into a lightweight class and declare your objects normally. After all, classes in Objective-C are just C-structs-(ish) with a lot of Apple magic thrown in.

CodaFi
  • 43,043
  • 8
  • 107
  • 153
  • Classes are not “just C-structs with a little Apple magic thrown in.” Under the modern runtime, they are not structs at all. – rob mayoff Aug 08 '12 at 04:02
  • So what are classes if not a (complicated) struct (id, objc_class, isa, @defs) then? – CodaFi Aug 08 '12 at 04:08
  • okay cool.. so really what I should do is create a lightweight class that just stores values. this can then be the object of an array in another class that can handle all the methods that will sort the array. Dose that make sense to you? – HurkNburkS Aug 08 '12 at 04:22
  • You'd probably want a controller type of object. One that manages the array, instead of the array managing itself. Subclass NSObject. – CodaFi Aug 08 '12 at 04:24
  • @CodaFi Read about [non-fragile ivars](http://www.sealiesoftware.com/blog/archive/2009/01/27/objc_explain_Non-fragile_ivars.html). Especially the “How it works” section at the end. – rob mayoff Aug 08 '12 at 04:26
  • OK, but how is this not Apple magic operating on a struct? Perhaps "Apple" and "Compiler" should be synonyms? – CodaFi Aug 08 '12 at 04:32
  • 1
    @rob while that may be the case sometimes, you can still actually create a strict with a `isa` pointer as it's first member and you can be darned sure that it will work just fine as an objective C object.. Where things get weird is subclassing. – Richard J. Ross III Aug 08 '12 at 04:33
  • 1
    @RichardJ.RossIII You can get a block of memory any way you want, put a class reference in its first four bytes, then cast the pointer to an object pointer. So what? I could do that with a union. Is a class therefore a union? I could do it with a carefully-crafted `float` or `double`. Is a class therefore a floating-point type? – rob mayoff Aug 08 '12 at 04:40
  • 1
    @CodaFi I can create a struct-typed variable on the stack, but I can't create a class instance on the stack. I can use `offsetof` on a struct field, but I can't use it on a class instance variable. I can create an array of structs (e.g. `MyStruct *p = malloc(10 * sizeof *p)`), but I can't create an array of instances. When I access a struct field, the compiler generates code that hardcodes the field offset at compile time, but when I access an instance variable, the compiler generates code that looks up the field offset at runtime. How exactly is a class a struct? – rob mayoff Aug 08 '12 at 04:44
  • I can use `sizeof` on a struct, but I can't use `sizeof` on an instance. I can embed a struct in another struct, but I can't embed a class in a struct or another class (except by subclassing). – rob mayoff Aug 08 '12 at 04:47
  • I feel like we're all talking past each other. Surely we all know about the basic definition of an objc object being just a typdef'd pointer to a struct (id). But then again, the compiler offers up lots of little changes and tweaks, as you mention, which sets classes apart from structs (else what would be the point of @interfaces?). And Rich is right. Even NSObject keeps an isa pointer. – CodaFi Aug 08 '12 at 04:47
  • The modern runtime uses a C-ism for convenience and perhaps backward-compatibility; that doesn't mean a class *is* a struct. I have written code that defines a struct with bitfields for convenient direct access to the sign, exponent, and mantissa of a double. Does that mean that a double is a struct? – rob mayoff Aug 08 '12 at 04:58
  • Now if I had actually said a class ***is*** a struct, then I could be strung up. I've changed it to reflect both attitudes. And what's to stop me from getting the double value of an object? – CodaFi Aug 08 '12 at 05:02
-3

Dont put objective C objects in structs. Thats why Obj C supports classes. You are coding C.

deleted_user
  • 3,817
  • 1
  • 18
  • 27
  • the thing thats confusing me is I want to create an array of object that contain the variables initalised in the class.. I had planned to just make a struct of variables then use the struct in the array.. how can i do this without a struct – HurkNburkS Aug 08 '12 at 03:31
  • Classes are contained by classes in objective C not structs. – deleted_user Aug 08 '12 at 03:39
  • I guess then the thing confusing me is how do I create an array of the classes that contain the variables. – HurkNburkS Aug 08 '12 at 03:41
  • Classes are just structs, albeit with more fancy compiler nonsense. And what's wrong with coding in C? I can write an NSString or an NSCFString, and it don't make a gal-durn difference to the compiler (with proper casting). – CodaFi Aug 08 '12 at 03:42
  • Clearly this is not the intent of the question. – deleted_user Aug 08 '12 at 04:06
  • Its pretty clear that if you knew C or objective C either that you would not be asking this question. So I assumed that you are trying to mix obj C pointers and C pointers because you dont understand objective C constructs. If you are an advanced programmer trying to mix C and obj C pointers, I think you will be advanced enough to find that documentation. I was trying to help you with objective C. In OBJECTive C, Objects contain other objects, structs are not typically used. The ARC error you got was there for a reason. – deleted_user Aug 08 '12 at 04:23