Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why use typeof() instead of the object type when avoiding block retain cycles?

Tags:

objective-c

I have seen this code snippet frequently when other ObjC developer are breaking a retain cycle.

__weak typeof(self) weakSelf = self;
[someObject doThingWithCompletion:^{
    typeof(weakSelf) strongSelf = weakSelf;
    [strongSelf awesomeThingOne];
    [strongSelf moreAwesome];
}];

Why the use of the typeof() macro? Is this unique to blocks?

My initial thought is that self's type might not be known (that seems mostly impossible, but let's pretend...). If the type is unknown, then why not declare weakSelf with an id: __weak id weakSelf = self;?

My second thought is that it's defensive against subclassing, but that seems unlikely to cause an issue. Assume ObjTwo subclasses AwesomeObj and overrides the awesomeThingOne method. The fake code above should work just fine if self is an instance of ObjTwo or AwesomeObj.

like image 761
edelaney05 Avatar asked Feb 11 '23 15:02

edelaney05


1 Answers

The typeof() macro allows a few things.

First of all, personally, I've created a code snippet with this sort of thing. Instead of having to type out:

__weak MyClass *weakSelf = self;

each and every time I want to set this up, substituting MyClass for the appropriate class, I can instead just start to type weakSelf and Xcode's autocomplete will try to offer my this line of code:

__weak typeof(self) weakSelf = self;

That's going to work every time.


Also, using typeof() gives us an explicit type and minimizes the code to rewrite if we ever change the actual type.

id is unsafe and it won't give us auto-completes on available methods and properties. After all, weakSelf is now of type id rather than of type MyClass.

And we can run into the same problem with subclasses where if I just default to MyClass, doing:

weak MyClass *weakSelf = self;

won't give me autocompletes on methods/properties that MySubClass added if self is actually MySubClass.


And use of typeof() isn't at all limited to blocks. I like to use typeof() in class methods.

Generally, the following is sufficient:

+ (instancetype)myInstance {
    return [[self alloc] init];
}

As instancetype and [self alloc] take care of getting the right subclass.

But suppose we want something more complex:

+ (NSMutableArray *)arrayOfInstances;

How can we make sure the objects we stick into the array are of the right type?

+ (NSMutableArray *)arrayOfInstances {
    NSMutableArray *instances = [NSMutableArray array];

    for (int i = 0; i < 10; ++i) {
        typeof([self alloc]) newObj = [[self alloc] init];
        newObj.someIntProperty = i;
        [instances addObject:newObj];
    }

    return instances;
}

We wouldn't want to use id as the type for newObj. We want to be certain that if we call:

[MyClass arrayOfInstances];

We get an array of MyClass objects, but if we call:

[MySubClass arrayOfInstances];

We get an array of MySubClass objects, etc.


We could also use typeof() if we wanted to instantiate an object from a dictionary that defined our object:

Class dynamicType = myDict[@"Class"];
typeof([dynamicType alloc]) myObject = [[dynamicType alloc] init];
myObject.foo = myDict[@"Foo"];
myObject.bar = myDict[@"Bar"];
like image 65
nhgrif Avatar answered Feb 14 '23 03:02

nhgrif