iosobjective-cpropertiesweak

Objective-C: work with weak references


I have a few classes: Book, Publisher, Author and Genre.

So here is the main class Book.h:

#import "Publisher.h"
#import "Author.h"
#import "Genre.h"

@interface Book : NSObject 

@property (nonatomic, strong) NSString *bookName;
@property (nonatomic, strong) Author *author;
@property (nonatomic, strong) Publisher *publisher;
@property (nonatomic, weak) Genre *genre;

- (instancetype)initWithBookName:(NSString *)name andAuthorName:(NSString *)authorName
      andPublisher:(NSString *)publisherName andGenreName:(__strong NSString *)genreName;
- (NSString *)description;
@end

and his implementation Book.m:

#import "Genre.h"
#import "Book.h"
#import <Foundation/Foundation.h>

@implementation Book 

- (instancetype)initWithBookName:(NSString *)name andAuthorName:(NSString *)authorName
  andPublisher:(NSString *)publisherName andGenreName:(__strong NSString *)genreName{
    self = [super init];
    if (self) {
        _bookName = [name copy];
        _author =  [[Author alloc] initWithAuthorName:authorName];
        _publisher = [[Publisher alloc] initWithPublisherName:publisherName];
        _genre = [[Genre alloc] initWithGenreName:genreName];
    }
    return self;
}

- (instancetype)init {
    return [self initWithBookName:@"unnamed" andAuthorName:@"unnamed" andPublisher:@"unnamed" andGenreName:@"unnamed"];
}

- (NSString *)description {
    return [NSString stringWithFormat: @"Book: %@, Author: %@, Genre: %@", self.bookName, self.author, self.genre];
}

@end

I have delegate class - Genre, so to avoid strong reference cycles, a Book's Genre property must be weak.

At this point in the Book.m initializer:

   _genre = [[Genre alloc] initWithGenreName:genreName];

it will be nil, because the Genre instance will be deallocated right after assignment.

According to Dan comment, here is my Genre.h:

#import <Foundation/Foundation.h>
@class Book;

@interface Genre : NSObject

@property (nonatomic, strong) NSString *genreName;
@property (nonatomic, strong) NSArray <Book *> *books;

- (instancetype)initWithGenreName:(NSString *)name andBooks:(NSArray <Book *>*)books;
- (instancetype)initWithGenreName:(NSString *)name;
- (NSString *)description;

@end

My question is "What is the best way to store genre object (genreName -> Genre constructor -> genre object) at weak property genre and how do I can store it without using constructor for assignment to weak property?".


SOLUTION: In my case it was collection of Genre and I take my weak property reference to one of objects from my collection.

Genre * genre1 = [[Genre alloc]initWithGenreName:@"Comedy"];
Genre * genre2 = [[Genre alloc]initWithGenreName:@"Drama"];
Genre * genre3 = [[Genre alloc]initWithGenreName:@"Fantastic"];
Genre * genre4 = [[Genre alloc]initWithGenreName:@"National"];

NSArray <Genre*> *genres = @[genre1, genre2, genre3, genre4];
Book *book1 = [[Book alloc] initWithBookName:@"Book #3!" andAuthorName:@"Grinch Burs" andPublisher:@"Ableton" andGenre:[genres objectAtIndex:0]];

Solution

  • The rule to remember is - strong properties increase the reference count, while weak ones do not - and when the reference count gets to 0, a proper is deallocated. So in the case of Genre - at your point in the code, there are no strong references to it so it is deallocated. The solution really is to have the Genres 'owned' by another class. This class would manage the genres, creating them and keeping strong references to them, perhaps in for eg an array of Genres. Your 'strong' genre would be passed in with the initializer, and then the weak reference is the correct approach, preventing a retain cycle, but the dealloc is prevented by the strong property that the Genre property already has - does that make sense?

    In a way it makes sense to think of your objects as needing an 'owner' class, where strong references are defined that keep them alive. Then when passed to other classes like your Book class, those have weak references, which prevents the retain cycle as you say. The book class isnt the owner, but someone else is - so it doesnt go away.