-2

I've noticed while working with Objective-C, the compiler throws error symbols enforcing the use of either self or an underscore when using a property which I think doesn't happen as harshly when using Swift. I'm now at the crossroads where I believe my view controller isn't being deallocated from the navigation stack. I've used self pretty heavily in order to silence the error symbols but I don't know if this has created retain cycles or not?

#import "MenuController.h"
#import "AppDelegate.h"
#import "CMUser.h"
#import "NotificationsSettingsController.h"
#import "PersonalInfoController.h"
#import "BeLive-Swift.h"
#import "LoginAndSecurityController.h"
#import "SplashController.h"


#define kCellSeparatorTag 100

@implementation SideMenuCell

- (void)layoutSubviews{
    [super layoutSubviews];
}

@end

@interface MenuController ()<UITableViewDataSource ,UITableViewDelegate>
@property (nonatomic, strong) NSArray *menuTitles;
@property (nonatomic, weak) IBOutlet  UITableView *tableView;
@property (weak, nonatomic) IBOutlet UILabel *usersNameLabel;
@property (weak, nonatomic) IBOutlet UILabel *usersEmailLabel;
@property (weak, nonatomic) IBOutlet UILabel *addPhotoLabel;
@property (weak, nonatomic) IBOutlet SettingsHeaderViewWithPhoto *settingsHeaderViewWithPhoto;
@end

@implementation MenuController

NSString *userFirstNameString;
NSString *userLastNameString;
NSString *userEmailString;
NSMutableString * usersFullNameString;

+ (instancetype)controller{
    static MenuController *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = ControllerFromMainStoryBoard([self description]);
    });
    return sharedInstance;
}



- (void) viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    [self.settingsHeaderViewWithPhoto.backButton addTarget:self action:@selector(popViewCon:) forControlEvents:UIControlEventTouchUpInside];
    [self.settingsHeaderViewWithPhoto.rightButton addTarget:self action:@selector(logoutTapped:) forControlEvents:UIControlEventTouchUpInside];
}

-(void)showUserInfoInHeaderView{
    if (userEmailString.length != 0) {
        self.settingsHeaderViewWithPhoto.emailSubLabel.text = userEmailString;
    } else {
        self.settingsHeaderViewWithPhoto.emailSubLabel.text = @"Email not available";
    }
    if (usersFullNameString.length != 0) {
        self.settingsHeaderViewWithPhoto.nameLabel.text = usersFullNameString;
    } else {
        self.settingsHeaderViewWithPhoto.nameLabel.text = @"Full name not available";
    }
    [self setMenuArrayBasedOnUserType];
    [self.tableView reloadData];
}

-(void)setMenuArrayBasedOnUserType{
    if (CMUser.currentUser.type == UserTypeArtist) {
        self.menuTitles = @[@[@"Notifications", @"Stripe Info", @"Personal Info", @"Login and Security", @"Invite a Friend", @"Help", @"Legal Agreement"]];
        [self.tableView reloadData];
    } else if (CMUser.currentUser.type == UserTypeViewer){
        self.menuTitles = @[@[@"Notifications", @"Personal Info", @"Login and Security", @"Invite a Friend", @"Help", @"Legal Agreement"]];
        [self.tableView reloadData];
    } else {
        [self showLoggedInAlert];
    };
}

-(void)showLoggedInAlert{
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Session Time Out"
                                                                   message:@"You must be logged in first."
                                                            preferredStyle:UIAlertControllerStyleAlert]; // 1
    UIAlertAction *firstAction = [UIAlertAction actionWithTitle:@"OK"
                                                          style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
                                                              NSLog(@"You pressed ok");
                                                              UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
                                                              SplashController *vc = [sb instantiateViewControllerWithIdentifier:@"SplashController"];
                                                              [self presentViewController:vc animated:YES completion:nil];

                                                          }];
    [alert addAction:firstAction];
    [self presentViewController:alert animated:YES completion:nil];
}

-(void)getLoggedInUserInfo{
    [NetworkManager callEndPoint:USER_DETAILS withDict:nil method:@"GET" JSON:YES success:^(id responseObject) {
        userFirstNameString = responseObject[@"data"][0][@"firstname"];
        userLastNameString = responseObject[@"data"][0][@"lastname"];
        userEmailString = responseObject[@"data"][0][@"emailAddress"];
        usersFullNameString = [NSMutableString stringWithString:userFirstNameString];
        [usersFullNameString appendString:@" "];
        [usersFullNameString appendString: userLastNameString];
        [self showUserInfoInHeaderView];
    } failure:^(id responseObject, NSError *error) {
        NSLog(@"callEndPoint error is: %@", error);
    }];
}


-(void)showProfilPhoto{
    if ([CMUser currentUser][@"imageUrl"] != NULL) {
        self.settingsHeaderViewWithPhoto.addPhotoLabel.text = @"";
        [self.settingsHeaderViewWithPhoto.userPhotoButton sd_setImageWithURL:[NSURL URLWithString:[CMUser currentUser][@"imageUrl"]]forState:UIControlStateNormal];
    } else {
        self.settingsHeaderViewWithPhoto.addPhotoLabel.text = @"Add Photo";
    }
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.navigationBarHidden = YES;
    [self.tableView registerClass:[SettingsTableViewCell class] forCellReuseIdentifier:@"MenuCell"];
    self.tableView.backgroundColor = [UIColor colorWithHex:0x222222];
    [self getLoggedInUserInfo];
    [self showProfilPhoto];
}



- (void)viewWillLayoutSubviews{
    [super viewWillLayoutSubviews];
    CGFloat revealWidth = ApplicationDelegate.drawerController.maximumLeftDrawerWidth;
    CGRect frame = self.view.frame;
    frame.size.width = revealWidth;
    self.view.frame = frame;
    self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    return self.menuTitles.count;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    NSMutableArray *menuItems = self.menuTitles[section];
    return menuItems.count;
}

- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

    NSArray *menuItems = self.menuTitles[indexPath.section];

    SettingsTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MenuCell"
                                                            forIndexPath:indexPath];

    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    cell.textLabel.text = menuItems[indexPath.row];

    UIView *separatorView = [cell.contentView viewWithTag:kCellSeparatorTag];
    if (!separatorView) {
        CGFloat revealWidth = ApplicationDelegate.drawerController.maximumLeftDrawerWidth;
        separatorView = [[UIView alloc] initWithFrame:CGRectMake(10, 43, revealWidth - 20, 1)];

        separatorView.backgroundColor = [UIColor darkGrayColor];
        [cell.contentView addSubview:separatorView];
    }

    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    [tableView deselectRowAtIndexPath:indexPath
                             animated:YES];
    if (indexPath.section == 0) {
        switch (indexPath.row) {
            case 0:
                [self notificationsTapped];
            default:
                break;
        }
        switch (indexPath.row) {
            case 1:
                //TODO: IF userrole == artist then show stripe info else if userrole == , show person information
                if (CMUser.currentUser.type == UserTypeArtist) {
                    NSLog(@"Show strip credit info");
                } else if (CMUser.currentUser.type == UserTypeViewer)  {
                    NSLog(@"Show personal info");
                    [self personalInfoTapped];
                }
                break;
            default:
                break;
        }
        switch (indexPath.row) {
            case 2:
                if (CMUser.currentUser.type == UserTypeArtist) {
                    NSLog(@"Show strip personal info");
                    [self personalInfoTapped];
                } else if (CMUser.currentUser.type == UserTypeViewer)  {
                    NSLog(@"Show login and security");
                    [self loginSecurityTapped];
                }
                break;
            default:
                break;
        }
        switch (indexPath.row) {
            case 3:
                if (CMUser.currentUser.type == UserTypeArtist) {
                    NSLog(@"Show login and security");
                    [self loginSecurityTapped];

                } else if (CMUser.currentUser.type == UserTypeViewer)  {
                    NSLog(@"Show Invite a friend");
                    [self inviteFriendTapped];
                }
                break;
            default:
                break;
        }
        switch (indexPath.row) {
            case 4:
                if (CMUser.currentUser.type == UserTypeArtist) {
                    NSLog(@"Show Invite a friend");
                    [self inviteFriendTapped];
                } else if (CMUser.currentUser.type == UserTypeViewer)  {
                    NSLog(@"Show Help");
                    //goto: webURL www.belive.com/help
                    [self helpTapped];
                }
                break;
            default:
                break;
        }
        switch (indexPath.row) {
            case 5:
                if (CMUser.currentUser.type == UserTypeArtist) {
                    NSLog(@"Show help");
                    //goto: webURL www.belive.com/help
                    [self helpTapped];
                } else if (CMUser.currentUser.type == UserTypeViewer)  {
                    NSLog(@"Show legal agreement");
                    //goto: webURL www.belive.com/legal
                    [self legalAgreementTapped];
                }
                break;
            default:
                break;
        }
        switch (indexPath.row) {
            case 6:
                if (CMUser.currentUser.type == UserTypeArtist) {
                    NSLog(@"Show legal agreement");
                    //goto: webURL www.belive.com/legal
                    [self legalAgreementTapped];

                } else if (CMUser.currentUser.type == UserTypeViewer)  {
                    NSLog(@"do nothing because there is not a value for this case.");
                }
                break;
            default:
                break;
        }
    }
}

- (void)notificationsTapped{
    NotificationsSettingsController *vc = [NotificationsSettingsController controller];
    [self.navigationController pushViewController:vc
                                         animated:YES];
}



- (void)popViewCon: (UIButton*)sender{
    [ApplicationDelegate toggleMenu];
}



- (void)logoutTapped: (UIButton*)sender{
    [ApplicationDelegate toggleMenu];
    [CMUser logOut];
    [ApplicationDelegate setController];
}


-(void)personalInfoTapped{
    UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Settings" bundle:nil];
    PersonalInfoController *vc = [sb instantiateViewControllerWithIdentifier:@"PersonalInfoController"];
    vc.usersProfilePhoto = self.settingsHeaderViewWithPhoto.userPhotoButton.imageView.image;
    [self.navigationController pushViewController:vc animated:YES];
}

-(void)loginSecurityTapped{
    UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Settings" bundle:nil];
    LoginAndSecurityController *vc = [sb instantiateViewControllerWithIdentifier:@"LoginAndSecurityController"];
    //LoginAndSecurityController *vc = [LoginAndSecurityController controller];
    [self.navigationController pushViewController:vc animated:YES];
}

-(void)inviteFriendTapped{
    UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Settings" bundle:nil];
    InviteFriendController
    *vc = [sb instantiateViewControllerWithIdentifier:@"InviteFriendController"];
    [self.navigationController pushViewController:vc animated:YES];
}

-(void)helpTapped{
    UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Settings" bundle:nil];
    HelpController *vc = [sb instantiateViewControllerWithIdentifier:@"helpController"];
    [self.navigationController pushViewController:vc animated:YES];
}

-(void)legalAgreementTapped{
    UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Settings" bundle:nil];
    LegalAgreementController *vc = [sb instantiateViewControllerWithIdentifier:@"legalController"];
    [self.navigationController pushViewController:vc animated:YES];
}
- (IBAction)addPhotoButtonTapped:(UIButton *)sender {
    [self configureAddPhotoActionSheet];
}

-(void)configureAddPhotoActionSheet{
    UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];

    [actionSheet addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {



        // Cancel button tappped do nothing.

    }]];

    [actionSheet addAction:[UIAlertAction actionWithTitle:@"Take a Photo" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {

        // take photo button tapped.
        [self takePhoto];

    }]];

    [actionSheet addAction:[UIAlertAction actionWithTitle:@"Choose Photo from Library" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {

        // choose photo button tapped.
        [self selectPhoto];

    }]];



    [self presentViewController:actionSheet animated:YES completion:nil];
}

- (void)takePhoto{
    UIImagePickerController *picker = [[UIImagePickerController alloc] init];
    picker.delegate = self;
    picker.allowsEditing = YES;
    picker.sourceType = UIImagePickerControllerSourceTypeCamera;

    [self presentViewController:picker animated:YES completion:NULL];
}

- (void)selectPhoto{
    UIImagePickerController *picker = [[UIImagePickerController alloc] init];
    picker.delegate = self;
    picker.allowsEditing = YES;
    picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;

    [self presentViewController:picker animated:YES completion:NULL];
}


//UIImagePickerDelegate Methods
/*
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info{
    UIImage *chosenImage = info[UIImagePickerControllerEditedImage];

    [self.profilePictureButton setImage:chosenImage forState:UIControlStateNormal];
    self.addPhotoLabel.text = @"";
    [self updateProfilePicture:chosenImage];
    [picker dismissViewControllerAnimated:YES completion:NULL];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {

    [picker dismissViewControllerAnimated:YES completion:NULL];
} */

- (void)updateProfilePicture:(UIImage*)image{
    NSData *imageData = UIImageJPEGRepresentation(image, 0.5);
    NSString *imageString = [NSString stringWithFormat:@"data:image/png;base64,%@", [imageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]];
    NSDictionary *params = @{@"ImageFileContent" : imageString,
                             @"ImageName" : @"image.jpg"
                             };
    NSString *uploadEndPoint = @"artist/add-artist-photo";
    MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:self.settingsHeaderViewWithPhoto.userPhotoButton animated:YES];
    [NetworkManager callEndPoint:uploadEndPoint
                        withDict:params
                          method:@"POST"
                            JSON:YES
                         success:^(id responseObject) {
                             NSLog(@"The response object has: %@", responseObject);
                             [CMUser currentUser][@"imageUrl"] = responseObject[@"data"][0][@"imageURL"];
                             [[CMUser currentUser] setCurrent];
                             [HUD hideAnimated:YES];
                         } failure:^(id responseObject, NSError *error) {
                             [HUD hideAnimated:YES];
                         }];
}

@end
Laurence Wingo
  • 3,912
  • 7
  • 33
  • 61
  • "I don't know if this has created retain cycles or not?" Then find out. Use the Memory Graph or the Leaks instrument to track down the retain cycle (if there is one). Don't guess. – matt Jul 22 '18 at 13:55
  • @matt yes I've spend quite some time using instruments as well as dealloc and deinit methods which also helped me track down my retain cycle. – Laurence Wingo Jul 22 '18 at 14:51
  • Excellent! So what _is_ the cycle in this case? Tell us! Don't throw all your code at us like spaghetti against a wall; show and explain to us what we need to know in order to understand the cycle, and no more. – matt Jul 22 '18 at 15:06

2 Answers2

3

This is used to synthesise the variable. It is necessary in objective c and swift works differently then swift. And it doesn't creat any retail cycle.

shivi_shub
  • 1,018
  • 1
  • 7
  • 15
  • I appreciate your answer because I understand your point of view. However, I'm a little weary after reading other sites after posting this question. Some articles claim that using self inside closures creates a retain cycles and we should set it to weak before using it within a closure if we can. I'll still vote you up for the input. – Laurence Wingo Jul 22 '18 at 14:55
  • 1
    The only time where you really want to use [weak self] is when you would create a strong reference cycle. A strong reference cycle is when there is a loop of ownership where objects end up owning each other (maybe through a third party) and therefore they will never be deallocated because they are both ensuring that each other stick around. – shivi_shub Jul 22 '18 at 15:20
0

After doing some reading, I've come to the realization that retain cycles can be created by delegates, using self within a closure, and if other objects are referencing your view controller in question. The problem I had was that this particular view controller is actually the root view controller of a navigation stack in which other view controllers were referencing it as well. Also I made a call to self within two block statements in the code above which also has the potential for creating a strong reference to itself.

Laurence Wingo
  • 3,912
  • 7
  • 33
  • 61