Can an object instance release itself? - ios

I need to create some objects that will be waiting for some event. When the waited event is triggered, the object makes some things, and then has no longer any reason to live.
I don't want to have to maintain a list of created objects, so I'd like to do something like this :
main() {
Waiter* myWaiter1 = [[Waiter alloc] initAndWaitForEvent:xxxxxxxxxx];
Waiter* myWaiter2 = [[Waiter alloc] initAndWaitForEvent:xxxxxxxxxx];
Waiter* myWaiter3 = [[Waiter alloc] initAndWaitForEvent:xxxxxxxxxx];
Waiter* myWaiter4 = [[Waiter alloc] initAndWaitForEvent:xxxxxxxxxx];
....
/* myWaiterx are retained */
/* I don't release them */
}
Waiter
- (void) catchSomeEvent:(...*)theEvent {
// do what is expected
[self release]; // Release self
/* the release is there */
}
Will this work, and work fine ?

I find it better when there’s somebody to take care of the waiters, but your code is fine. Objects can do this, there is no technical obstacle that would prevent it. Some classes from the standard library already do something similar, for example UIAlertView.
I don’t think that the static analyzer will like your current API, though. It will probably complain about leaks; it would be better to tweak the interface a bit.
#interface Waiter : NSObject {}
- (id) init;
- (void) startWaitingForEvent: (id) event;
#end
#implementation Waiter
- (void) startWaitingForEvent: (id) event
{
[self retain];
…
}
- (void) eventReceived
{
…
[self release];
}
#end
Then the memory management in user code looks better:
- (void) dispatchWaiters {
Waiter w1 = [[Waiter alloc] init];
[w1 startWaitingForEvent:…];
[w1 release];
}

An object can not suicide. It can either be killed by you(the code you sent to kill it), or by the professional killer NSAutoreleasePool. If you own it, you have to kill it.
Warning: If it doesn't die on time, the population will increase and will mess up the memory.
;-)

On some occasions [self release]; is used, for example for initializers (to force variables that a required in some way), an example:
SomeClass.m:
- (id)initWithString:(NSString *)string
{
self = [super init];
if (self)
{
if (string == nil)
{
[self release];
return nil;
}
// if required values are provided, we can continue ...
}
return self;
}
- (id)init
{
return [self initWithString:nil];
}
A caller would call this like:
- (void)testInitializer
{
SomeClass *classInstance1 = [[SomeClass alloc] initWithString:#"bla"];
// classInstance1 != nil, any method calls will work as expected ...
SomeClass *classInstance2 = [[SomeClass alloc] initWithString:nil];
// classInstance2 == nil, will ignore any method calls (fail silently)
SomeClass *classInstance3 = [[SomeClass alloc] init];
// classInstance3 == nil, will ignore any method calls (fail silently)
}
I would guess since the above works fine, you shouldn't have any issues, though it doesn't seem a very clean solution.

Related

Doubts about __bridge, _bridge_retain and _bridge_transfer

I have read about __bridge, _bridge_retain and _bridge_transfer and did some experiments. However the output does not coincide with what I was expecting. In particular, I have the following code:
#interface ViewController ()
#property (nonatomic, strong) NSString *test;
#end
#implementation ViewController
CFStringRef cfString;
- (void)viewDidLoad
{
[super viewDidLoad];
self.test = #"123";
cfString = (__bridge CFStringRef)self.test;
self.test = nil;
}
- (void)viewDidAppear:(BOOL)animated
{
NSLog(#"%#", cfString);
NSLog(#"%#", self.test);
}
I expect the program to crash, based on the following reasoning: _bridge does not transfer ownership, so while casting self.test to cfString, there is no retainCount increment. Once self.test is set to nil, ARC will step in and dealloc the string. So that portion of memory is freed, but cfString is still pointing there, resulting in a pointer bad access exception. In contrast to my reasoning, the output is 123 and null, and of course the program does not crash.
Moreover, if I replace
self.test = nil;
with
CFRelease(cfString);
I expect the program to crash as well due to a similar reasoning. Even stranger is that the output is now 123 and 123.
Can anyone kindly elaborate why? Btw, the term ownership always troubles me, some explanation will be greatly appreciated.
Your problem is that you're using a constant string. This is put straight into the programs memory so the reference is unexpectedly remaining valid despite the fact that it shouldn't. Use something less constant than a constant string and your program will brake like you think.
The problem is that you base your example on a literal NSString value.
In objective-C, constant NSString (constant values known at compile time) are never released. In fact, their main memory managment methods are like:
+ (id)allocWithZone:(NSZone *)zone {
id _uniqueInstance = [self _singletonInstanceOfClass];
if( _uniqueInstance == nil )
_uniqueInstance = [super allocWithZone:zone];
return _uniqueInstance;
}
- (id)copyWithZone:(NSZone *)zone {
(void)zone;
return self;
}
- (id)retain {
return self;
}
- (NSUInteger)retainCount {
return NSUIntegerMax; // denotes an object that cannot be released
}
- (oneway void)release {
//do nothing
return;
}
- (id)autorelease {
return self;
}
As you can see, releasing them is not possible.

CTCallCenter doesn't update the currentCalls property after I set callEventHandler

I'm trying to see if there are any ongoing calls, but I'm having trouble with keeping an instance of CTCallCenter as a property. Here's basically what I'm doing now that I'm debugging (everything is in MyClass):
-(void)checkForCurrentCalls
{
CTCallCenter *newCenter = [[CTCallCenter alloc] init];
if (newCenter.currentCalls != nil)
{
NSLog(#"completely new call center says calls in progress:");
for (CTCall* call in newCenter.currentCalls) {
NSLog(call.callState);
}
}
if (self.callCenter.currentCalls != nil) {
NSLog(#"property-call center says calls in progress:");
for (CTCall* call in self.callCenter.currentCalls) {
NSLog(call.callState);
}
}
}
My self.callCenter is a #property (nonatomic, strong) CTCallCenter *callCenter;. It has synthesized setter and getter. It's initialized in MyClass' init method:
- (id)init
{
self = [super init];
if (self) {
self.callCenter = [[CTCallCenter alloc] init];
}
return self;
}
If I call another of my methods before checkForCurrentCalls, then my self.callCenter.currentCalls stops updating the way it should. More precisely, the phonecalls I make (to myself) just keep piling on, so that if I've dialed and hung up three phonecalls I get printouts of three CTCalls being in the "dialing" state. The newCenter works as expected.
All I have to do to break it is call this:
- (void)trackCallStateChanges
{
self.callCenter.callEventHandler = ^(CTCall *call)
{
};
}
I have come across answers that say that CTCallCenter has to be alloc-init'd on the main queue. I have since then taken care to only call my own init on the main queue, using dispatch_async from my app delegate:
dispatch_async(dispatch_get_main_queue(), ^{
self.myClass = [[MyClass alloc] init];
[self.myClass trackCallStateChanges];
}
My checkForCurrentCalls are later called in applicationWillEnterForeground.
I don't understand why just setting the callEventHandler-block breaks it.
Everything is tested on iphone 5 ios 7.1.2.
This has been reproduced in a new project. Filed a bug report on it.

arc and no-arc issues

This is my code
__weak KDObject *obj = [KDObject fetchObj] ;
NSLog(#"%#", obj) ; // I think it should be nil, but it is not
obj.i = 10 ;
NSLog(#"%d", obj.i) ;
In KDObject.m
#implementation KDObject
+ (instancetype)fetchObj
{
return [[self alloc] init] ;
}
#end
the result is the same whatever KDOjbect.m is compile with -fno-objc-arc flag or without -fno-objc-arc flag
Anybody has ideas why obj is not nil ?
Related to your Q and to your answer:
-fectchObject is a method not belonging to any method family with ownership transfer. Therefore ARC has to ensure that returning the reference is safe. That means that losing the strong reference in the local scope of -fetchObject does not give up the last reference.
One way to accomplish this is to use the autorelease pool. But ARC does not guarantee that the ARP is used. Moreover it tries not to use the ARP, because it is the solution with the highest memory pressure.
So the things happening depends of the compiler implementation, attributes set to the method and what the compiler sees in source code (esp. implementation of -fetchObject). So you should not rely on returning in ARP.
__weak is guaranteed to be nil, if the object is destroyed. But it is not guaranteed that the object is destroyed in the earliest possible moment. This is subject of optimization.
From the docs about __weak
__weak specifies a reference that does not keep the referenced object alive. A weak reference is set to nil when there are no strong
references to the object.
Whether it's __weak or not KDObject *o = [[KDObject alloc] init] creates an object so o is not nil.
__weak is something realated to memory management. If none of strong objects are pointing to a weak object, it would be released from memory.
- (void)loadView
{
[super loadView];
TestObject *obj_ = [[TestObject alloc] init];
pObj = obj_;
if(pObj == nil)
{
NSLog(#"pObj_ is not nil");
}
__weak TestObject *obj2_ = [[TestObject alloc] init];
if(obj2_ == nil)
{
NSLog(#"obj2_ is nil");
}
__weak TestObject *obj3_ = [TestObject createInstance];
if(obj3_ == nil)
{
NSLog(#"obj3_ is nil");
}
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if(pObj == nil)
{
NSLog(#"pObj is nil");
}
}
KudoCC requested this code. I used LLVM5.1. If I use -fno-objc-arc to TestObject.h, the objc3_ became not nil.

Removing an Observer when it gets Deallocated

I have the following 2 classes:
EventDispatcher:
#interface EventDispatcher()
-(id)initEventDispatcher;
-(NSMutableArray*)getSubscriptionsToEvent:(EVENT_TYPE)eventType;
-(NSNumber*)getKeyToEvent:(EVENT_TYPE)eventType;
#end
#implementation EventDispatcher
static EventDispatcher* eventDispatcher;
// Singleton.
+(EventDispatcher*)instance
{
if (eventDispatcher == nil)
{
eventDispatcher = [[EventDispatcher alloc] initEventDispatcher];
}
return eventDispatcher;
}
-(id)initEventDispatcher
{
self = [super init];
if (self)
{
eventSubscriptions = [[NSMutableDictionary alloc] init];
}
return self;
}
// Let anyone subscribe to an event. Return the EventSubscriber so they can dispatch events if needed, and to be able to unsubscribe.
-(EventSubscriber*)subscribe:(EVENT_TYPE)eventType :(void(^)(id package))operateEvent
{
// Create the object.
EventSubscriber* eventSubscriber = [[EventSubscriber alloc] initEventSubscriber:eventType :operateEvent];
// Now get the list it belongs to (we sort subscriptions in a dictionary so that when we dispatch an event, it's fast (we don't need to iterate through all EventSubscribers to find who subscribe to an event).
NSMutableArray* subscriptionsToThisEvent = [self getSubscriptionsToEvent:eventType];
if(subscriptionsToThisEvent == nil)
{
// If the list is nil, no one has subscribed to it yet, so make that list and add it to the dictionary.
subscriptionsToThisEvent = [[NSMutableArray alloc] init];
NSNumber* key = [self getKeyToEvent:eventType];
[eventSubscriptions setObject:subscriptionsToThisEvent forKey:key];
[subscriptionsToThisEvent release];
}
// Add the EventSubscriber to the subscription list.
[subscriptionsToThisEvent addObject:eventSubscriber];
[eventSubscriber release];
return eventSubscriber;
}
-(void)unsubscribe:(EventSubscriber*)eventSubscriber
{
// Get the list it belongs to, and remove it from that list.
EVENT_TYPE eventType = [eventSubscriber getEventType];
NSMutableArray* subscriptionsToThisEvent = [self getSubscriptionsToEvent:eventType];
if (subscriptionsToThisEvent != nil)
{
[subscriptionsToThisEvent removeObject:eventSubscriber];
}
}
-(void)dispatch:(EVENT_TYPE)eventType :(id)package
{
NSMutableArray* subscriptionsToThisEvent = [self getSubscriptionsToEvent:eventType];
// If no one has subscribed to this event, it could be nil, so do nothing.
if (subscriptionsToThisEvent != nil)
{
// Otherwise, let them all know that the event was dispatched!
for (EventSubscriber* eventSubscriber in subscriptionsToThisEvent)
[eventSubscriber dispatch:package];
}
}
// Helper methods to get stuff (lists, keys) from event types.
-(NSMutableArray*)getSubscriptionsToEvent:(EVENT_TYPE)eventType
{
NSNumber* key = [self getKeyToEvent:eventType];
NSMutableArray* subscriptionsToThisEvent = [eventSubscriptions objectForKey:key];
return subscriptionsToThisEvent;
}
-(NSNumber*)getKeyToEvent:(EVENT_TYPE)eventType
{
return [NSNumber numberWithInt:eventType];
}
-(void)dealloc
{
[eventSubscriptions release];
[super dealloc];
}
#end
EventSubscriber:
#import "EventSubscriber.h"
#implementation EventSubscriber
-(id)initEventSubscriber:(EVENT_TYPE)newEventType :(void(^)(id package))newOperateEvent
{
self = [super init];
if (self)
{
operateEvent = [newOperateEvent copy];
eventType = newEventType;
}
return self;
}
-(void)dispatch:(id)package
{
operateEvent(package);
}
-(EVENT_TYPE)getEventType
{
return eventType;
}
-(void)dealloc
{
[operateEvent release];
[super dealloc];
}
#end
Onto the big question: How do I unburden a programmer who is using this system with having to unsubscribe from an event during deallocation? When multiple classes are using this system, programmers will have to remember to unsubscribe during deallocation (if not an earlier time), or REALLY bad/weird/unexpected things could happen (I would prefer a compile-time check, or a big, obvious, debuggable crash, but more-so the former). Ideally, I'd like to restructure this system (or do anything) so that when an object is deallocated, the EventDispatcher gracefully handles it.
One quick fix is to have objects allocate EventSubscribers directly, then in the EventSubscriber constructor, it subscribes itself to EventDispatcher (but that's obviously bad, maybe make EventDispatcher's stuff static? Ugh now we're just getting worse).
Side notes:
I'm not using ARC, but, that does not matter here (at least I think it does not, if there are ARC-based solutions, I'd like to hear them).
I do plan on adding a method in EventDispatcher to be able to remove EventSubscribers by those who did the subscription (so now when subscribing, objects will have to pass 'self'). I also plan on making the enumerated EVENT_TYPE use strings, but that's a different topic altogether.
I also plan on translating a lot of my code (including these classes) to C++. So I'd appreciate a conceptual solution as opposed to Objective-C specific solutions.
So, is this possible?
Thanks a bunch!

Objective-C multiple nested initWith for a custom class

I'm pretty new to Objective-C and I have a question.
I have created a custom class and tried to create overloads for Initialization:
- (id)init
{
if (self = [super init]) {
[self setIsCurrentCar:NO];
}
return self;
}
-(id) initWithID:(NSInteger)id {
if(self = [self init]) {
[self setID:id];
}
return self;
}
-(id) initWithID:(NSInteger)id CarYear:(NSString *)year {
if(self = [self initWithID:id]) {
[self setCarYear:year];
}
return self;
}
Let's say at one point, I call the -(id) initWithIDCarYear method.
I'd like to know the code above is structurally correct.
In this code, self is set for 3 times. Is there a better
solution?
Do I have memory leak in this code? (using ARC)
Do I have to check for if(self = ...) always or it is a
redundant code?
Thank you
#Edit
Is the following code better?
-(id) initWithID:(NSInteger)id CarYear:(NSString *)year {
if (self = [super init]) {
[self setIsCurrentCar:NO];
[self setID:id];
[self setCarYear:year];
}
return self;
}
While your code is ok, i would structure the init-calls in the reverse order, where the most detailed one is the designated initializer and the more general ones would bubble some default values up:
-(id) initWithID:(NSInteger)id
CarYear:(NSString *)year
{
if(self = [super init]) {
_year = year;
_id = id;
}
return self;
}
-(id)initWithID:(NSInteger)id
{
return [self initWithID:id CarYear:#"unset"];
}
-(id)init
{
return [self initWithID:0];
}
if calling one of the more general initializer would generate an illegal state, you could instead throw an error to prohibit using it.
let's assume, a car needs to have a ID, but not a year. It would be ok to use initWithID but using init would lead to an inconsistent state, so we want to force not to use it:
-(id)init
{
[NSException raise:NSInternalInconsistencyException
format:#"You must use -initWithID: or -initWithID:CarYear:", NSStringFromSelector(_cmd)];
return nil;
}
In this code, self is set for 3 times. Is there a better solution?
see above
Do I have memory leak in this code? (using ARC)
No, everything is fine
Do I have to check for if(self = ...) always or it is a redundant code?
As I showed you: you can call different init methods in a chain. just the last in that chain needs to perform that.
-(id) initWithID:(NSInteger)id CarYear:(NSString *)year {
if (self = [super init]) {
[self setIsCurrentCar:NO];
[self setID:id];
[self setCarYear:year];
}
return self;
}
You should not use setters on self in init-methods, see Apple's docs.
I'd like to know the code above is structurally correct.
Yes. I don't see any problem with it.
In this code, self is set for 3 times. Is there a better solution?
That's pretty normal. I wouldn't bother changing that.
Do I have memory leak in this code? (using ARC)
No.
Do I have to check for if (self = ...) always or it is a redundant
code?
You don't have to, but you definitely should. See this question for details.
It looks like you have one mandatory initialized variable and two that are effectively optional.
I'd recommend implementing an init method and two #property()s for the ID and carYear. That reduces the # of initializers and better reflects that usage contract of the class.
From my little knowledge... tried to create overloads for Initialization this statement should not been used here.
As typically overload means same name multiple arguments, but in obj-c we do not follow this. In obj-c overloading is faked by naming the parameters.
So here you have 3 different sets of code, and each one is called some other method. And you do not have a memory leak as you are not allocating memory thrice for same object, instead you are initializing it.

Resources