NSNotificationCenter get notifications on NSMutableDictionary addObject - objective-c

How can I get notifications with NSNotificationCenter when object is added to NSMutableDictionary?
Also are there any notifications for immutable NSDictionary?

The the beauty of inheritance :)
#define kSomeDictionarySetAValue #"SomeDictionarySetAValue"
#interface SomeDictionary : NSMutableDictionary
#end
#implementation SomeDictionary
- (void) setValue:(id)value forKey:(NSString *)key
{
[super setValue:value forKey:key];
[[NSNotificationCenter defaultCenter] postNotificationName:kSomeDictionarySetAValue
object:self];
}
#end

Related

Object with callback block destroyed after init

I have such a helper class.
#interface CustomOnScreenshot : NSObject;
#property (copy) void (^finishedCallback)(id sender);
-(instancetype)initWithCallback: (void (^)(id sender))callback;
+(instancetype)onScreenshot:(void (^)(id sender))callback;
#end
#implementation CustomOnScreenshot
-(instancetype)initWithCallback: (void (^)(id sender))callback{
self = [super init];
if (self) {
self.finishedCallback = callback;
[self subscribeEvent];
}
return self;
}
+(instancetype)onScreenshot:(void (^)(id sender))callback{
CustomOnScreenshot * onScreenShot = [self new];
[onScreenShot setFinishedCallback:callback];
return onScreenShot;
}
-(void)subscribeEvent{
NSLog(#"CustomOnScreenshot subscribeEvent");
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(doOnScreenShot:) name:UIApplicationUserDidTakeScreenshotNotification object:nil];
}
-(void)unsubscribeEvent{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationUserDidTakeScreenshotNotification object:nil];
}
-(void)doOnScreenShot: (id)sender{
if (self.finishedCallback) {
self.finishedCallback(sender);
}
}
-(void)dealloc{
NSLog(#"CustomOnScreenshot dealloc");
[self unsubscribeEvent];
}
The problem is that if you use it as intended, then the object is immediately destroyed
- (void)viewDidLoad {
[super viewDidLoad];
[CustomOnScreenshot onScreenshot:^(id sender) {
// CUSTOM code
}];
}
Log:
CustomOnScreenshot subscribeEvent
CustomOnScreenshot dealloc
All works only when I use the result in property, but I find this excessive
#property (strong, nonatomic) CustomOnScreenshot * customOnScreenshot;
- (void)viewDidLoad {
[super viewDidLoad];
self.customOnScreenshot = [CustomOnScreenshot onScreenshot:^(id sender) {
// CUSTOM code
}];
}
If you don't have a strong reference to the CustomOnScreenshot instance, then the object will be deallocated by ARC as as soon as +onScreenshot: finishes execution.
That's why the #property fixes it.
If you don't want an #property, then I'd suggest a singleton.

NSTask subclass error - launchPath only defined for abstract class

I'm trying to create a subclass of NSTask, that is augmented with a name and an activityDescription property, for the purpose of displaying status, in a UI in an OSX desktop application.
However, when I try to set the launchPath or arguments property on my subclass instance, I get these errors:
launchPath only defined for abstract class. Define -[Task launchPath]!
arguments only defined for abstract class. Define -[Task arguments]!
So, I defined setLaunchPath: and setArguments: as shown below, and I still get the same errors.
By the way, NSData stringValue is defined in NSData+Additions.h:
- (NSString *) stringValue {
return [[NSString alloc] initWithData:self encoding:NSUTF8StringEncoding];
}
Any help is greatly appreciated!
Task.h
#import <Foundation/Foundation.h>
#protocol TaskDelegate;
#interface Task : NSTask {
NSString *_launchTask;
NSArray *_arguments;
}
#property (weak, nonatomic) id<TaskDelegate> delegate;
#property (strong, nonatomic) NSString *name;
#property (strong, nonatomic) NSString *activityDescription;
//To be implemented by subclasses, should be called just before launch
- (void) setupTask;
- (BOOL) isConfigured;
//Launches the task - includes any setup required before the task is launched
- (void) launchAndWait;
- (void) setLaunchPath:(NSString *)launchPath;
- (void) setArguments:(NSArray *)arguments;
#end
#protocol TaskDelegate <NSObject>
- (void) task:(Task *)task didReceiveTaskError:(NSString *)errorString;
#end
Task.m
#import "Task.h"
#import "NSData+Additions.h"
#interface Task ()
#end
#implementation Task
- (id) init
{
self = [super init];
if (self) {
}
return self;
}
- (void) errorOccurred:(NSNotification *)notification {
if (_delegate) {
NSData *readData = [[notification userInfo] objectForKey:NSFileHandleNotificationDataItem];
NSString *outputString = [readData stringValue];
if (outputString.length)
[_delegate task:self didReceiveTaskError:outputString];
}
}
- (void) setupTask
{
//To be implemented by subclasses
}
- (BOOL) isConfigured
{
return (self.launchPath != nil);
}
//Process task conguration and execution is optional - if process task is not configured, this returns immediately
- (void) launchAndWait
{
//Allow setupTask to be used, without actually launching
if (![self isConfigured])
return;
NSPipe *errorPipe = [NSPipe pipe];
self.standardError = errorPipe;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(errorOccurred:)
name:NSFileHandleReadToEndOfFileCompletionNotification
object:[errorPipe fileHandleForReading]];
[[errorPipe fileHandleForReading] readToEndOfFileInBackgroundAndNotify];
[self launch];
[self waitUntilExit];
//Tear down
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void) setLaunchPath:(NSString *)launchPath
{
_launchTask = launchPath;
}
- (void) setArguments:(NSArray *)arguments
{
_arguments = arguments;
}
#end
I decided to use the composition approach and use NSTask inside my Task class, instead of inheritance, and it's working well.
Here is the updated class:
Task.h
#import <Foundation/Foundation.h>
#protocol TaskDelegate;
#interface Task : NSObject {
__weak id<TaskDelegate> _delegate;
NSTask *_processTask;
}
#property (weak, nonatomic) id<TaskDelegate> delegate;
#property (strong, nonatomic) NSString *name;
#property (strong, nonatomic) NSString *activityDescription;
//To be called just before launch
- (void) setupTask;
- (BOOL) processTaskConfigured;
//Launches the task - includes any setup required before the task is launched
- (void) launchAndWait;
//////////////////////////////////////////////////////////////////////////////////////////////// Process task delegation
#property (readonly) int terminationStatus;
- (BOOL) isRunning;
- (void) terminate;
#end
#protocol TaskDelegate <NSObject>
- (void) task:(Task *)task didReceiveTaskError:(NSString *)errorString;
#end
Task.m
#import "Task.h"
#import "NSData+Additions.h"
#interface Task ()
//Delegating to NSTask
#property (strong, nonatomic) NSTask *processTask;
#end
#implementation Task
- (id) init
{
self = [super init];
if (self) {
self.processTask = [NSTask new];
}
return self;
}
- (void) errorOccurred:(NSNotification *)notification {
if (_delegate) {
NSData *readData = [[notification userInfo] objectForKey:NSFileHandleNotificationDataItem];
NSString *outputString = [readData stringValue];
if (outputString.length)
[_delegate task:self didReceiveTaskError:outputString];
}
}
- (void) setupTask
{
//To be implemented by subclasses
}
- (BOOL) processTaskConfigured
{
return (_processTask.launchPath != nil);
}
//Process task conguration and execution is optional - if process task is not configured, this returns immediately
- (void) launchAndWait
{
if (!_processTask.launchPath)
return;
NSPipe *errorPipe = [NSPipe pipe];
_processTask.standardError = errorPipe;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(errorOccurred:)
name:NSFileHandleReadToEndOfFileCompletionNotification
object:[errorPipe fileHandleForReading]];
[[errorPipe fileHandleForReading] readToEndOfFileInBackgroundAndNotify];
[_processTask launch];
[_processTask waitUntilExit];
//Tear down
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
////////////////////////////////////////////////////////////////////////////////////////// Process task delegation
- (int) terminationStatus
{
if ([self processTaskConfigured]) {
return [_processTask terminationStatus];
}
return 0;
}
- (BOOL) isRunning
{
return [_processTask isRunning];
}
- (void) terminate
{
if ([self processTaskConfigured]) {
[_processTask terminate];
}
}
#end

pass NSString variable to other class with NSNotification

I want to pass a NSString from one class to another class and add that NSString to an NSMutableArray in my second class. I'm believe i can use NSNotification for this, but i don't know how to pass an variable over notification. My code would something like this:
//class1.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property(strong,nonatomic)NSString *variableString;
#end
//class1.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize variableString = _variableString;
- (void)viewDidLoad
{
[super viewDidLoad];
[self setVariableString:#"test"];
[[NSNotificationCenter defaultCenter] postNotificationName: #"pasteString" object: _variableString];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
#end
//class2.h
#import <UIKit/UIKit.h>
#interface ViewController2 : UIViewController
#property(strong,nonatomic)NSMutableArray *arr;
#end
//class2.m
#import "ViewController2.h"
#interface ViewController2 ()
#end
#implementation ViewController2
#synthesize arr = _arr;
- (void)viewDidLoad:(BOOL)animated
{
[super viewDidLoad];
if(_arr == nil)
{
_arr = [[NSMutableArray alloc]init];
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(incomingNotification:) name:#"pasteString" object:nil];
// Do any additional setup after loading the view.
}
- (void) incomingNotification:(NSNotification *)notification{
NSString *theString = [notification object];
[_arr addObject:theString];
}
#end
In sender class you can post a notification with an object with something like this:
[[NSNotificationCenter defaultCenter] postNotificationName: NOTIFICATION_NAME object: myString];
The listener or receiver class has to register for the notification:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(incomingNotification:) name:NOTIFICATION_NAME object:nil];
The method incomingNotification is:
- (void) incomingNotification:(NSNotification *)notification{
NSString *theString = [notification object];
...
}
EDIT
When you post the notification from "ViewController", is "ViewController2" loaded?
Send your Notification like this:
if(isComingFromHowToUseThisApp) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"noteFromVideoPlayer"
object:#"fromHowToUseThisApp"];
}else {
[[NSNotificationCenter defaultCenter] postNotificationName:#"noteFromVideoPlayer"
object:#"fromVideoPlayerViewiPad"];
}
Receive your notification like this:
// Reset the default value of table view in master split view
- (void)defaultCellSelectionOfTableView:(NSNotification *) objNotification {
// We need to change the default selection of row
NSString *noticeFromVideoPlayer = [objNotification object];
}// end defaultCellSelectionOfTableView

iOS: Memory leak in simple MVC model

I've build (must be simple...) MVC model, but I still have memory leak when pushing back button.
Model class: .h
#interface Nominal : NSObject {
int nominalID;
NSString *nominal;
NSString *nominalImg;
NSString *nominalName;
}
#property(nonatomic)int nominalID;
#property(nonatomic,retain)NSString *nominal;
#property(nonatomic,retain)NSString *nominalImg;
#property(nonatomic,retain)NSString *nominalName;
#end
.m
#implementation Nominal
#synthesize nominal,nominalID,nominalImg,nominalName;
-(void)dealloc
{
[self.nominal release];
[self.nominalImg release];
[self.nominalName release];
}
#end
I do release the strings as well.
In my view class I populate it so:
.h
#interface Nominals : UIViewController {
...
NSMutableArray *nominalsArr;
...
}
#property(retain,nonatomic)NSMutableArray *nominalsArr;
.m
- (void)viewWillAppear:(BOOL)animated
{
[[self navigationController]setToolbarHidden:YES animated:YES];
DBAccess *dbAccsess=[[DBAccess alloc]init];
self.nominalsArr=[dbAccsess returnNominals:subCountryID];
[dbAccsess closeDataBase];
[dbAccsess release];
[super viewWillAppear:animated];
}
- (void)dealloc
{
[nominalsArr release];
[self.navigationController release];
[super dealloc];
}
Looks like I do release the whole bundle of holy things, but when I push pack button from this view to previous, the memory leak pops up:
What I'm doing wrong?
You help is utterly appreciated.
You've forgotten a [super dealloc] in [Nominal -dealloc]. Also, don't call [self.navigationController release] as that property is already handled by the superclass (UIViewController).

Having problems with adding objects to NSMutableArray

I am having problem with adding objects to NSMutableArray *array.
// Controller.m
#import "Controller.h"
#implementation Controller
- (void)parser:(NSString *)string{
[array addObject:string];
NSLog(#"answerArray(1): %#",[array objectAtIndex:1]);
[array retain];
}
#end
// Controller.h
#import <Foundation/Foundation.h>
#interface Controller : NSObject {
NSMutableArray *array;
}
- (void)parser:(NSString *)string;
#end
NSLog(#"answerArray(1): %#",[array objectAtIndex:1]);
Results: answerArray(1): (null)
First off, you're over-retaining the array.
Second, you didn't provide the code for initializing the array, so I guess it's not allocated and initialized. This will cause the code to message a nil object and thus return nil.
You should create an init method for the Controller object, and allocate a new NSMutableArray object (and retain it).
Also, a proper dealloc to release the array.
NSMutabaleArray starts at index 0
Here is the method I added to Controller class:
- (id)init {
self = [super init];
if(self){
array = [[NSMutableArray alloc] init];
}
return self;
}
- (void)dealloc {
[array release];
[super dealloc];
}