Removing an Observer when it gets Deallocated - ios

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!

Related

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.

NSMutableArray thread safety

In my app I'm accessing and changing a mutable array from multiple threads. At the beginning it was crashing when I was trying to access an object with objectAtIndex, because index was out of bounds (object at that index has already been removed from array in another thread). I searched on the internet how to solve this problem, and I decided to try this solution .I made a class with NSMutableArray property, see the following code:
#interface SynchronizedArray()
#property (retain, atomic) NSMutableArray *array;
#end
#implementation SynchronizedArray
- (id)init
{
self = [super init];
if (self)
{
_array = [[NSMutableArray alloc] init];
}
return self;
}
-(id)objectAtIndex:(NSUInteger)index
{
#synchronized(_array)
{
return [_array objectAtIndex:index];
}
}
-(void)removeObject:(id)object
{
#synchronized(_array)
{
[_array removeObject:object];
}
}
-(void)removeObjectAtIndex:(NSUInteger)index
{
#synchronized(_array)
{
[_array removeObjectAtIndex:index];
}
}
-(void)addObject:(id)object
{
#synchronized(_array)
{
[_array addObject:object];
}
}
- (NSUInteger)count
{
#synchronized(_array)
{
return [_array count];
}
}
-(void)removeAllObjects
{
#synchronized(_array)
{
[_array removeAllObjects];
}
}
-(id)copy
{
#synchronized(_array)
{
return [_array copy];
}
}
and I use this class instead of old mutable array, but the app is still crashing at this line: return [_array objectAtIndex:index]; I tried also this approach with NSLock, but without a luck. What I'm doing wrong and how to fix this?
I believe this solution is poor. Consider this:
thread #1 calls count and is told there are 4 objects in the array.
array is unsynchronized.
thread #2 calls removeObjectAtIndex:2 on the array.
array is unsynchronized.
thread #1 calls objectAtIndex:3 and the error occurs.
Instead you need a locking mechanism at a higher level where the lock is around the array at both steps 1 and 5 and thread #2 cannot remove an object in between these steps.
You need to protect (with #synchronized) basically all usage of the array. Currently you only prevent multiple threads from concurrently getting objects out of the array. But you have no protection for your described scenario of concurrent modification and mutation.
Ask yourself why you're modifying the array on multiple threads - should you do it that way or just use a single thread? It may be easier to use a different array implementation or to use a wrapper class that always switches to the main thread to make the requested modification.

iOS Storing a pointer

In my ios application i want to store an object's reference. This object can be an instance from interface A,B,C or D. I know that it's always going to be one from these four, but never know which one. How can i represent this object in my code?
Sincerely, Zoli
Represent it as type id.
id ptr;
Also, be aware of the possibility that you can specialise type id to some protocol.
id <SomeProtocol>;
what i understand that you should make a shared instance of that object.
example:-
make a new class named SavedReference.
and write in implementation file this code
Code:
static SavedReference *sharedInstance = nil; //if using iOS5 or above no need to nil it.
+(SavedReference*)sharedInstance
{
#synchronized(self)
{
if(!sharedInstance)
{
sharedInstance = [[self alloc]init];
return sharedInstance;
}
}
return nil;
}
-(id)init
{
self = [super init];
if(self)
{
//initialize variables
}
return self;
}
And Call this class as [[SavedReference sharedInstance] write ur method]

Automatic Reference Counting and finalize

Quick question: I use lots of NSObject derived classes and am wondering how to properly cleanup class properties that may own instances of other classes (in the snippet below this is an array of custom class instances). Is my usage of new and finalize below correct?
My understanding is that new is a convenience method that calls both alloc and init, and finalize gets called before dealloc - at least this is what I have gleaned from reading the docs. Do I have this right?
Thanks for any tips/best practices, etc!
- (id)new {
waffleArray = [[NSMutableArray alloc] initWithCapacity:kCellCount];
for (int i = 0; i < kCellCount; i++) {
WaffleCell * cell = [WaffleCell new];
[waffleArray addObject:cell];
}
return self;
}
// clean up
- (void)finalize {
[waffleArray removeAllObjects];
waffleArray = nil;
[super finalize];
}
new on NSObject is a class method, not an instance method as you have it. Also I don't really see why you'd overload new. It would be more common to overload init so something like this:
- (id)init {
if ((self = [super init])) {
waffleArray = [[NSMutableArray alloc] initWithCapacity:kCellCount];
for (int i = 0; i < kCellCount; i++) {
WaffleCell * cell = [WaffleCell new];
[waffleArray addObject:cell];
}
}
return self;
}
As for finalize, you really don't need to do that. This is what Apple says about it:
The garbage collector invokes this method on the receiver before disposing of the memory it uses. When garbage collection is enabled, this method is invoked instead of dealloc.
With ARC enabled you wouldn't need to do anything and since the garbage collector will not be running anyway, finalize won't get called anyway. ARC will automatically generate code which will release waffleArray in dealloc for you, which is enough for proper memory management in this case because waffleArray's retain count will then drop to 0, be deallocated itself which will go and release the objects in the array.

Can an object instance release itself?

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.

Resources