Will there be retain cycle? - objective-c

Here is some abstract code. There is base class "BaseClass" and child class "MyClass"
BaseClass
#property (nonatomic, copy) BlockType block;
- initBaseClassWithBlock:(BlockType)block {
self = [super init];
if (self) {
self.block = block;
}
return self;
}
MYClass : BaseClass
#property (strong, nonatomic) NSString *myString;
- (instancetype)init {
self = [super initBaseClassWithBlock:^{
NSLog(#"%#", self.myString);
}
return self;
}
Will there be retain cycle in MyClass init method?

Yes; that is a retain cycle.
You can check by pasting the code into a simple project and then using Xcode 8's memory graph debugger.
Copy/paste this compilable version of your code then build and run. It'll run forever and do nothing. Click the little object graph debugger icon (the three circles with lines in them) and it'll show you that you have a strong reference from the instance of sub back to itself via the block.
#interface Base:NSObject
#property (nonatomic, copy) dispatch_block_t block;
#end
#interface Sub:Base
#property (strong, nonatomic) NSString *myString;
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
Sub *s = [[Sub alloc] init];
}
dispatch_main();
return 0;
}
#implementation Base
- initBaseClassWithBlock:(dispatch_block_t)block
{
self = [super init];
if (self) {
self.block = block;
}
return self;
}
#end
#implementation Sub
- (instancetype)init {
if (self = [super initBaseClassWithBlock:^{
NSLog(#"%#", self.myString);
}]) {
;
}
return self;
}
#end

Related

Using custom method to add objects to NSMutableArray Objective C

I am new to Objective C, I'm trying to add objects to NSMutableArray that I can use multiple times on my project. I have a Model class as
History.h
import
#interface History : NSObject
#property (nonatomic) NSString *itemName;
#property (nonatomic) int quantity;
#property (nonatomic) double total;
#property (nonatomic) NSDate *purchaseDate;
- (instancetype)initWithName: (NSString*)itemName withQuantity:(int)quantity withTotal:(double) total withPurchaseDate:(NSDate*) purchaseDate;
#end
History.m
#import "History.h"
#implementation History
-(instancetype)initWithName: (NSString*)iName withQuantity:(int)iQuantity withTotal:(double) iTotal withPurchaseDate:(NSDate*) iPdate {
self = [super init];
if(self) {
self.itemName = iName;
self.quantity = iQuantity;
self.quantity = iQuantity;
self.purchaseDate = iPdate;
}
return self;
}
#end
Repository.h
#import <Foundation/Foundation.h>
#interface Repository : NSObject
#property (nonatomic) NSMutableArray *itemHistory;
-(void) pushToArray:(NSString *)name withQuantity:(int)qty withTotal:(double) total withPurchaseDate:(NSDate*) pDate;
#end
Repository.m
#import "Repository.h"
#import "History.h"
#interface Repository()
//#property (nonatomic) NSMutableArray *itemHistory;
#end
#implementation Repository
-(NSMutableArray *) itemHistory {
if(_itemHistory == nil) {
_itemHistory = [[NSMutableArray alloc] init];
}
return _itemHistory;
}
This is my method that I want to use to add objects to the MutableArray.
-(void) pushToArray:(NSString *)name withQuantity:(int)qty withTotal:(double) total withPurchaseDate:(NSDate*) pDate {
self.itemHistory = [[NSMutableArray alloc] init];
History *obj = [[History alloc] init];
obj.itemName = name;
obj.quantity = qty;
obj.total = total;
obj.purchaseDate = pDate;
[self.itemHistory addObject:obj];
}
#end
Thank you for your help in advance.
Every time you call pushToArray:... you are replacing the existing itemHistory with a new, empty, mutable array. You'll only ever see the last item to be pushed in that array.
However, you also don't need to lazily initialize _itemHistory. Just create an instance in your init method and be done with it. Saves confusion and refactoring hell.
In your Repository class, simply implement the designated initializer:
- (instancetype) init
{
if (self=[super init]) {
_itemHistory = [[NSMutableArray alloc] init];
}
return self;
}
Uncomment the property:
#interface Repository()
#property (nonatomic) NSMutableArray *itemHistory;
#end
And remove this from the -pushToArry:... method:
//self.itemHistory = [[NSMutableArray alloc] init];
If it still doesn't work, you need to show how you are logging the failure.

performSelector:#selector withObject:nil afterDelay: not calls method (Objective C)

I tried to implement alarm and person relations in the morning through delegate and to simulate delays after each ring used performSelector:#selector withObject:nil afterDelay. But no function calls performed, but when I use simply performSelector: - it works fine. All in main thread. Can somebody explain that? Here is my code:
#import <Foundation/Foundation.h>
#protocol MyDelegateProtocol <NSObject>
#optional
- (void) wakeUp;
- (void) standUp;
#end
#import <Foundation/Foundation.h>
#import "myDelegateProtocol.h"
#interface Alarm : NSObject
#property (nonatomic, weak) id <MyDelegateProtocol> delegate;
#property (nonatomic, assign) NSUInteger ringsLeft;
#property (nonatomic, assign) SEL selector;
- (void) setAlarm: (CGFloat) delay;
- (void) fiveMinutesMore;
- (id) initWithRings: (NSUInteger) rings;
- (void) doAlarm;
#end
#import "Alarm.h"
#implementation Alarm
#synthesize delegate;
#synthesize selector;
- (id) initWithRings:(NSUInteger)rings
{
self = [super init];
if (self) {
self.ringsLeft = rings;
}
return self;
}
- (void)setAlarm:(CGFloat)delay {
if (delegate) {
if (self.ringsLeft>0) {
self.ringsLeft--;
selector = #selector(wakeUp);
} else {
selector = #selector(standUp);
}
}
if ([delegate respondsToSelector:selector]) {
[self performSelector:#selector(doAlarm)];
// [self performSelector:#selector(doAlarm) withObject:nil afterDelay:delay];
}
}
- (void)doAlarm {
[delegate performSelector:selector];
}
- (void)fiveMinutesMore {
[self setAlarm:1.0];
}
#end
#import <Foundation/Foundation.h>
#import "Alarm.h"
#interface Person : NSObject <MyDelegateProtocol>
#property (nonatomic,assign) NSUInteger chanceToWakeUp;
#property (nonatomic,strong) Alarm *myAlarm;
- (void) goSleepFor: (CGFloat) seconds;
- (id) init;
#end
#import "Person.h"
#implementation Person
#synthesize chanceToWakeUp;
#synthesize myAlarm;
-(void) goSleepFor:(CGFloat)seconds
{
NSLog(#"Going for a sleep");
[myAlarm setAlarm:seconds];
}
-(void) wakeUp
{
NSLog(#"Ringing...");
NSUInteger randomNumber = arc4random_uniform(6);
if (randomNumber>chanceToWakeUp) {
chanceToWakeUp++;
NSLog(#"Person takes five minutes more to sleep");
[myAlarm fiveMinutesMore];
} else {
[self standUp];
}
}
-(void) standUp
{
NSLog(#"Person stand's up");
}
-(id) init
{
self = [super init];
if (self) {
chanceToWakeUp=0;
myAlarm = [[Alarm alloc] initWithRings:5];
myAlarm.delegate=self;
}
return self;
}
#end
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
#autoreleasepool {
Person *tiredPerson = [[Person alloc] init];
[tiredPerson goSleepFor:10.0f];
}
return 0;
}
I think your program is terminating before your delay time out. Try adding:
[[NSRunLoop currentRunLoop] run]
after your goToSleepFor: call.
To use:
[self performSelector:#selector(doAlarm:) withObject:nil afterDelay:delay];
the method must be defined to take an argument:
- (void)doAlarm:(id)object {
or
[self performSelector:#selector(doAlarm) withObject:nil afterDelay:delay];
the method must be defined to take an argument:
- (void)doAlarm {
Performing a selector with delay queues the operation on the current run loop. You didn't start any run loops in your program. Your program simply calls some functions synchronously which return and then your program exits.
if your code is running when the run loop is in a mode other than the default mode, try this:
[self performSelector:#selector(doAlarm) withObject:nil
afterDelay:delay
inModes:#[[[NSRunLoop currentRunLoop] currentMode]]];

'Property not found' in subclass constructor

I have the next code:
GenericClass.h:
#interface GenericClass : NSObject
#property (nonatomic) char tile_inicial;
#property (strong, nonatomic) NSString *color;
GenericClass.m
#implementation GenericClass
-(id)init
{
self = [super init];
return self;
}
- (id)initTodo:(char) tile
{
_tile_inicial = tile;
return self;
}
#end
Animals.h:
#import "GenericClass.h"
#interface Animals : GenericClass
#property (nonatomic) bool fly;
#property (strong, nonatomic) NSString *sound;
#end
#interface Cow : Animals
#property (nonatomic) bool daLeche;
#end
Animals.m
#import "Animals.h"
#implementation Animals
-(id)init
{
self = [super init];
return self;
}
- (id)initAnimal:(bool)fly :(NSString *) sound :(char) tile
{
_fly = fly;
_sound = sound;
self = [super initTodo: tile];
return self;
}
#end
#implementation Cow
-(id)init
{
self = [super initAnimal: false :#"Muu Muu": 'v'];
if (self) {
_daLeche = true;
}
return self;
}
#end
The issue is I would like to call directly from the Cow constructor to any property (for example color or sound) and initialize it.
But if I try to do it without call the parent constructor it returns: "Property not found"
Any idea?
Thanks in advance

How to implement initWith method passing custom object as an argument?

For instance I have following class:
#interface PrivateInfo: NSObject
- (instancetype)initWithPrivateInfoObject:(PrivateInfo *)pi;
#end
#implementation PrivateInfo
{
#private
NSString *name;
NSString *creditCardID;
}
- (instancetype)initWithPrivateInfoObject:(PrivateInfo *)pi
{
// how to init current object with passed pi ?
}
- (PrivateInfo *)copy
{
// how to create a copy of current object?
}
#end
Assuming ARC:
.h:
#interface PrivateInfo: NSObject <NSCopying>
- (instancetype)initWithPrivateInfoObject:(PrivateInfo *)pi;
#end
.m:
#implementation PrivateInfo {
NSString *name;
NSString *creditCardID;
}
- (instancetype)initWithPrivateInfoObject:(PrivateInfo *)pi
{
self = [super init];
if (self) {
name = pi->name;
creditCardID = pi->creditCardID;
}
return self;
}
- (PrivateInfo *)copyWithZone:(NSZone *)zone {
PrivateInfo *res = [[PrivateInfo alloc] initWithPrivateInfoObject:self];
return res;
}
#end
You don't need #private in the #implementation block for declaring ivars. And use the NSCopying protocol is you want to copy objects.

Why can't I populate my controller with items?

I'm using an ItemController to provide a list of items to use in a tableview. I can't seem to populate the controller though, and I'm not sure why.
Here's the code for the controller class:
.h
#import <Foundation/Foundation.h>
#class Item;
#interface ItemController : NSObject
#property (nonatomic, copy) NSMutableArray *items;
- (NSUInteger)countOfList;
- (Item*)objectInListAtIndex:(NSUInteger)theIndex;
- (void)addItem:(Item *)item;
#end
.m
#import "ItemController.h"
#import "Item.h"
#interface ItemController ()
#end
#implementation ItemController
- (NSUInteger)countOfList {
return [self.items count];
}
- (Item *)objectInListAtIndex:(NSUInteger)theIndex {
return [self.items objectAtIndex:theIndex];
}
- (void)addItem:(Item *)item {
[self.items addObject:item];
}
#end
Item.m
#implementation Item
-(id)initWithName:(NSString *)name{
self = [super init];
if (self) {
_name = name;
return self;
}
return nil;
}
#end
I'm using the following code to populate the list:
ItemController* controller = [[ItemController alloc] init];
for (NSString* key in raw_data) {
NSLog(key); // This outputs the keys fine
[controller addItem:[[Item alloc] initWithName:key]];
}
NSLog([NSString stringWithFormat:#"%d",[controller countOfList]]); // Always 0
You need to initialize the array in the init methond.
- (id)init {
self = [super init];
if (self) {
self.items = [[NSMutableArray alloc] init];
}
return self;
}
You need to initialize your variable items. In your init method, call self.items = [NSMutableArray new]; and also change your array property from copy to retain.
I also believe your class ItemController should be of kind UIViewController and not NSObject.
#interface ItemController : UIViewController
You don't initialise the _items instance variable anywhere, so it's always nil. The result of any integer-returning method called on nil will be 0, so you see that the count is 0.