ARC Objective-C delegation through a C++ abstract layer - c++

I'm programming simple cross platform C++ layer (dylib) that gets implemented by Objective-C. The parts that are platform specific are included through platform macros.
The NSUserNotificationCenter requires the delegation pattern for handling specific actions, clicking on the notification for example. The issue I'm facing is that as soon as I execute send, the notification is sent but the instance unloads right after that. Thus the onclick notification action never gets called (didActivateNotification) instead it crashes for a bad pointer. How can I make this work?
Note:
SomeObjectiveC.mm is a class located in my Application. AppleUserNotificationManager.m is initialized by NotificationManager+apple.mm and both are located in my Dylib.
SomeObjectiveC.mm
Notification *notification = new Notification;
notification->setTitle("Foo bar notification");
notification->setMessage("Hello world!");
NotificationManager *notificationManager = new NotificationManager;
notificationManager->send(notification);
NotificationManager+apple.mm
#include "notificationManager+apple.hpp"
bool NotificationManager::send(Notification *notification)
{
AppleUserNotificationManager *notificationManager = [[AppleUserNotificationManager alloc] init];
NSString *title = [NSString stringWithUTF8String:notification->getTitle().c_str()];
NSString *message = [NSString stringWithUTF8String:notification->getMessage().c_str()];
if (notification->getSoundName().empty()) {
[notificationManager sendWithTitle:title andMessage:message];
}
NSString *soundName = [NSString stringWithUTF8String:notification->getSoundName().c_str()];
[notificationManager sendWithTitle:title andMessage:message andSound: soundName];
return true;
}
AppleUserNotificationManager.m
#import "AppleUserNotificationManager.h"
#implementation AppleUserNotificationManager
#synthesize userNotification;
- (id)init
{
[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate: self];
userNotification = [[NSUserNotification alloc] init];
self = [super init];
return self;
}
/**
* #param NSUserNotificationCenter center
* #param NSUserNotification notification
*
* #return bool
*/
- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification{
return YES;
}
/**
* #param NSUserNotificationCenter center
* #param NSUserNotification notification
*/
- (void) userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification
{
NSString *notificationText = [notification informativeText];
NSString *urlRegEx = #"(http|https)://((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*))+";
NSPredicate *urlTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", urlRegEx];
if ([urlTest evaluateWithObject:notificationText]) {
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:notificationText]];
}
}
/**
* #param NSString title
* #param NSString message
* #param NSString soundName
*/
- (void)sendWithTitle:(NSString *)title andMessage:(NSString *)message andSound:(NSString *)soundName{
userNotification.title = title;
userNotification.informativeText = message;
userNotification.soundName = soundName;
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification: userNotification];
}
#end

Used self pointer before allocated?
Could below change fix the problem?
- (id)init
{
self = [super init];
[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate: self];
userNotification = [[NSUserNotification alloc] init];
return self;
}

Related

Getting Crash with AWSS3PutObjectRequest

I am using the AWS SDK like this way
Properties declaration :
#property (nonatomic, strong)AWSStaticCredentialsProvider *credentialsProvider;
#property (nonatomic, strong)AWSServiceConfiguration * configuration;
#property (nonatomic, strong)AWSS3 *s3;
#property (nonatomic, strong) AWSS3PutObjectRequest *putrequest;
And Implementation like this way
AccessKeys * accessKeys = [ECSGlobals sharedInstance].accessKeys;
self.credentialsProvider = [AWSStaticCredentialsProvider credentialsWithAccessKey:accessKeys.accessKeyId secretKey:accessKeys.secretAccessKey];
self.configuration = [AWSServiceConfiguration configurationWithRegion:AWSRegionUSEast1 credentialsProvider:self.credentialsProvider];
self.s3 = [[AWSS3 alloc] initWithConfiguration:self.configuration];
self.putrequest = [AWSS3PutObjectRequest new];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *savedImagePath = [documentsDirectory stringByAppendingPathComponent:imageName];
NSData *imageData = UIImageJPEGRepresentation(image, 0.2);
[imageData writeToFile:savedImagePath atomically:NO];
long long fileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:savedImagePath error:nil][NSFileSize] longLongValue];
self.putrequest.bucket = bucketName;
self.putrequest.key = imageName;
self.putrequest.contentType = #"image/jpeg";
self.putrequest.body = [NSURL fileURLWithPath:savedImagePath];
self.putrequest.contentLength = [NSNumber numberWithLongLong:fileSize];
[[self.s3 putObject:self.putrequest] continueWithBlock:^id(BFTask *task) {
NSLog(#"Amazon error : %#", [task error]);
return nil;
}];
But I am getting crash
[CFError retain]: message sent to deallocated instance 0x7f2daff0
I have checked all properties are strong. Not sure why I am getting this crash.
I found the solution. We are getting this issue as some how saved image path become nil before coming in use. So there is no image data sending to AWS.
After fixing this, images are uploading successfully.

Dynamically creating getters and setters for #dynamic properties causing infinite loop

Currenlty I have an issue with ocmock for unit testing because some #dynamic properties have no getters or setters when being mocked. I'm using class_addmethod to add the getters and setters for all #dynamic properties. My issue is this
void accessorSetter(id self, SEL _cmd, id newValue)
{
NSString *method = NSStringFromSelector(_cmd);
id value = [newValue copy];
// remove set prefix from string
NSString *anID = [[method stringByReplacingCharactersInRange:NSMakeRange(0, 3) withString:#""] stringByReplacingOccurrencesOfString:#":" withString:#""];
anID = [anID stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:[[anID substringWithRange:NSMakeRange(0, 1)] lowercaseString]];
[self setValue:value forKey:anID];
}
causes an infite loops since setValue calls the setter. I think I can use c++ syntax like self->somevar = value to avoid the infinite loop. My question is how do I do this assigment when the name of the variable is a string? anID is the name of the variable and i can't do self->anID = aValue cuz anID is not a property. How do I convert it to a variable name? Or how can I set the property without creating the infinite loop?
You can use object_setInstanceVariable or object_setIvar and object_getInstanceVariable. You can take a look at the Objective C Runtime Reference for more information.
EDIT:
If you are using ARC, you can't use object_setInstanceVariable. You'll be stuck with object_setIvar.
EDIT 2: See this answer if you are using ARC.
Example usage:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#interface Test : NSObject
#property (nonatomic, strong) NSString *test;
#end
#implementation Test
- (id)init {
if (self = [super init]) {
self.test = #"asdasd";
}
return self;
}
- (void)setTest:(NSString*)test {
Ivar var_desc = object_getInstanceVariable(self, [#"_test" cStringUsingEncoding:NSUTF8StringEncoding], NULL);
NSLog(#"Setting _test to %#", test);
object_setIvar(self, var_desc, test);
}
#end
int main(int argc, char *argv[])
{
Test *tst = [[Test alloc] init];
NSLog(#"prop: %#", tst.test);
return 0;
}
Output:
2013-12-11 18:19:32.038 ivar_tst[97090:507] Setting _test to asdasd
2013-12-11 18:19:32.040 ivar_tst[97090:507] prop: asdasd

issue is running and stopping CFRunLoop

i have situation where i need to wait till one block is get completed and then only move forward with my code for that i use CFRunLooprun and stop this is how do it i will explain more things in comment in my code
[self fatchAllEvent]; // BLOCK IS IN THIS METHOD
NSLog(#"loop will start");
CFRunLoopRun();
NSLog(#"LOOP IS STOOPED");
-(void)fatchAllEvent{
events = [[NSMutableArray alloc]init];
// // Get the appropriate calendar
NSCalendar *calendar = [NSCalendar currentCalendar];
eventStore = [[EKEventStore alloc] init];
if ([eventStore respondsToSelector:#selector(requestAccessToEntityType:completion:)])
{
// __block typeof (self) weakSelf = self; // replace __block with __weak if you are using ARC
dispatch_async(dispatch_get_main_queue(), ^{
[eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error)
{
if (granted)
{
[events removeAllObjects];
NSLog(#" granted");
NSLog(#"User has granted permission!");
// Create the start date components
NSDateComponents *twoYearAgoComponents = [[NSDateComponents alloc] init];
twoYearAgoComponents.year = -2;
NSDate *oneDayAgo = [calendar dateByAddingComponents:twoYearAgoComponents
toDate:[NSDate date]
options:0];
// Create the end date components
NSDateComponents *twoYearFromNowComponents = [[NSDateComponents alloc] init];
twoYearFromNowComponents.year = 2;
NSDate *oneYearFromNow = [calendar dateByAddingComponents:twoYearFromNowComponents
toDate:[NSDate date]
options:0];
// Create the predicate from the event store's instance method
NSPredicate *predicate = [eventStore predicateForEventsWithStartDate:oneDayAgo
endDate:oneYearFromNow
calendars:nil];
// Fetch all events that match the predicate
events =(NSMutableArray*) [eventStore eventsMatchingPredicate:predicate];
NSLog(#"The content of array is%#",events);
}
else
{
NSLog(#"Not granted");
}
NSLog(#"LOOP WILL STOP"); // THIS GETS PRINT
CFRunLoopStop(CFRunLoopGetCurrent()); // BUT LOOP IS NOT STOPPING HERE SO MY APP JUST GET HANGED ;
}];
});
}
else
{
[events removeAllObjects];
NSLog(#"Autometiclly granted permission!");
// Create the start date components
NSDateComponents *twoYearAgoComponents = [[NSDateComponents alloc] init];
twoYearAgoComponents.year = -2;
NSDate *oneDayAgo = [calendar dateByAddingComponents:twoYearAgoComponents
toDate:[NSDate date]
options:0];
// Create the end date components
NSDateComponents *twoYearFromNowComponents = [[NSDateComponents alloc] init];
twoYearFromNowComponents.year = 2;
NSDate *oneYearFromNow = [calendar dateByAddingComponents:twoYearFromNowComponents
toDate:[NSDate date]
options:0];
// Create the predicate from the event store's instance method
NSPredicate *predicate = [eventStore predicateForEventsWithStartDate:oneDayAgo
endDate:oneYearFromNow
calendars:nil];
// Fetch all events that match the predicate
events =(NSMutableArray*) [eventStore eventsMatchingPredicate:predicate];
NSLog(#"The content of array is%#",events);
}
}
You can't do that this way - you have to call your function, and then in the completion block of the async dispatch, at the end, call a function that continues with what you want to do.
In your above code you mix up asynchronous programming with synchronous execution, that won't work.

Custom objects in array stored in a plist

I've got some custom class objects that i store in an array, this array is then stored in a dictionary with a key. This is then saved and will be loaded when the app is loaded/ reloaded.
Now the issues is that i can't seem to get the custom objects back out, when i load the array it comes back as empty. I must be doing something wrong with either writing or loading the data but i can't figure out what exactly.
Here's the code i have so far:
CustomClass.H file:
#import <Foundation/Foundation.h>
#interface PersonClass : NSObject <NSCoding>
#property (nonatomic,strong) NSString *personName;
#property (nonatomic,strong) NSString *personNo;
#property (nonatomic,strong) NSString *personNotes;
#property (nonatomic) BOOL switchPersonality;
#property (nonatomic) BOOL switchChemistry;
#property (nonatomic) BOOL switchLooks;
#property (nonatomic) BOOL switchHumour;
#property (nonatomic) int personRating;
#property (nonatomic,strong) NSNumber *personRecord;
- (id)initWithName:(NSString *)iPersonName PersonNo:(NSString *)iPersonNo PersonNotes:(NSString *)iPersonNotes SwitchChemistry:(BOOL *)iSwitchChemistry SwitchHumour:(BOOL *)iSwitchHumour SwitchLooks:(BOOL *)iSwitchLooks SwitchPersonality:(BOOL *)iSwitchPersonality PersonRating:(int *)iPersonRating PersonRecord:(NSNumber *)iPersonRecord;
#end
Custom Class.M file:
- (void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:personName forKey:#"pName"];
[aCoder encodeObject:personNo forKey:#"pNo"];
[aCoder encodeObject:personNotes forKey:#"pNotes"];
[aCoder encodeInt:personRating forKey:#"pRating"];
[aCoder encodeObject:personRecord forKey:#"pRecord"];
[aCoder encodeBool:switchChemistry forKey:#"sChemistry"];
[aCoder encodeBool:switchHumour forKey:#"sHumour"];
[aCoder encodeBool:switchLooks forKey:#"sLooks"];
[aCoder encodeBool:switchPersonality forKey:#"sPersonality"];
}
-(id)initWithCoder:(NSCoder *)aDecoder{
if (self = [super init]){
self.personName = [aDecoder decodeObjectForKey:#"pName"];
self.personNo = [aDecoder decodeObjectForKey:#"pNo"];
self.personNotes = [aDecoder decodeObjectForKey:#"pNotes"];
self.personRating = [aDecoder decodeIntForKey:#"pRating"];
self.personRecord = [aDecoder decodeObjectForKey:#"pRecord"];
self.switchChemistry = [aDecoder decodeBoolForKey:#"sChemistry"];
self.switchHumour = [aDecoder decodeBoolForKey:#"sHumour"];
self.switchLooks = [aDecoder decodeBoolForKey:#"sLooks"];
self.switchPersonality = [aDecoder decodeBoolForKey:#"sPersonality"];
}
return self;
}
The save Method:
- (void)saveData{
// get paths from root direcory
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
// get documents path
NSString *documentsPath = [paths objectAtIndex:0];
// get the path to our Data/plist file
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"data.plist"];
NSLog(#"PList Path %#",plistPath);
//new array
NSMutableArray *newEntries = [[NSMutableArray alloc]init];
NSLog(#"NEW ENTRIES BEFORE %#",newEntries);
for (PersonClass *person in peopleEntries) {
//encode the object
[NSKeyedArchiver archivedDataWithRootObject:person];
// add the object to the entries
[newEntries addObject:person];
}
NSLog(#"NEW ENTRIES AFTER %#",newEntries);
[newEntries writeToFile:plistPath atomically:YES];
// create dictionary with arrays and their corresponding keys
NSDictionary *plistDict = [NSDictionary dictionaryWithObjects: [NSMutableArray arrayWithObjects:newEntries, recordId, nil] forKeys:[NSMutableArray arrayWithObjects: #"peopleEntries",#"recordId", nil]];
NSLog(#"DICTIONARY IS IN SAVE %#",plistDict);
NSString *error = nil;
// check if plistData exists
if(plistDict)
{
// write plistData to our Data.plist file
[plistDict writeToFile:plistPath atomically:YES];
}
else
{
NSLog(#"Error in saveData: %#", error);
}
NSLog(#"Save RUN");
}
The load Method:
- (void)loadData{
// get paths from root direcory
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
// get documents path
NSString *documentsPath = [paths objectAtIndex:0];
// get the path to our Data/plist file
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"data.plist"];
NSLog(#"PList Path %#",plistPath);
// check to see if data.plist exists in documents
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath])
{
// return without loading
NSLog(#"RETURNING");
return;
}
// get saved dictionary
NSDictionary *dictionaryTemp = [[NSDictionary alloc]initWithContentsOfFile:plistPath];
NSLog(#"DICTIONARY IS LOAD %#",dictionaryTemp);
if (!dictionaryTemp)
{
NSLog(#"Error reading plist:");
}
// temporary array
NSMutableArray *holderOne = [dictionaryTemp objectForKey:#"peopleEntries"];
// array to be populated
NSMutableArray *holderTwo = [[NSMutableArray alloc]init];
NSLog(#"HOLDER ONE IS BEFORE %#",holderOne);
NSLog(#"HOLDER TWO IS BEFORE %#",holderTwo);
// go through the array and pull out person classes
for (NSData *person in holderOne) {
// temp array
NSData *entry = [holderOne objectAtIndex:person]; // check the object at index might be an issue???
NSLog(#"ENTRY IS %#",entry);
//deencode the object
[NSKeyedUnarchiver unarchiveObjectWithData:entry];
// add the object to the entries
[holderTwo addObject:entry];
}
NSLog(#"HOLDER ONE IS AFTER %#",holderOne);
NSLog(#"HOLDER TWO IS AFTER %#",holderTwo);
// assign values
peopleEntries = [NSMutableArray arrayWithArray:holderTwo];
NSLog(#"DICTIONARY IS AFTER ADDING %#",dictionaryTemp);
recordId = [NSNumber numberWithInt:[[dictionaryTemp objectForKey:#"recordId"]integerValue]];
NSLog(#"recordId is %#",recordId);
NSLog(#"LOAD RUN");
}
OK i finally discovered the issue, i was extending my class from the NSObject subclass which does not implement the NSCoder protocol so i was encoding and decoding my object in the incorrect way. It should have been done in the following way:
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
self.personName = [decoder decodeObjectForKey:#"pName"];
self.personNo = [decoder decodeObjectForKey:#"pNo"];
self.personNotes = [decoder decodeObjectForKey:#"pNotes"];
self.personRating = [decoder decodeObjectForKey:#"pRating"];
self.switchChemistry = [decoder decodeBoolForKey:#"sChemistry"];
self.switchHumour = [decoder decodeBoolForKey:#"sHumour"];
self.switchLooks = [decoder decodeBoolForKey:#"sLooks"];
self.switchPersonality = [decoder decodeBoolForKey:#"sPersonality"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:personName forKey:#"pName"];
[encoder encodeObject:personNo forKey:#"pNo"];
[encoder encodeObject:personNotes forKey:#"pNotes"];
[encoder encodeObject:personRating forKey:#"pRating"];
[encoder encodeBool:switchChemistry forKey:#"sChemistry"];
[encoder encodeBool:switchHumour forKey:#"sHumour"];
[encoder encodeBool:switchLooks forKey:#"sLooks"];
[encoder encodeBool:switchPersonality forKey:#"sPersonality"];
}
These objects were then added into an array and i was looping through each object in the array and decoding. This was not correct you can simply encode or decode the entire array with te objects as so:
Decode:
NSData *peopleData;
peopleEntries = [NSKeyedUnarchiver unarchiveObjectWithData:peopleData];
Encode:
NSMutableArray *peopleEntries;
NSData *personData = [NSKeyedArchiver archivedDataWithRootObject:[[GlobalData sharedGlobalData]peopleEntries]];

fast enumeration and selecting objects

I've an array (tempList) populated with records read from a sqlite data source. I want to sort these records based on 'region'. So I've set up the following NSMUtableArrays: _Asia, _Africa, _CentralAmerica, _southAmerica.
The method compiles without any errors. Where the 'if' statements return true, it executes the 'addObject' but the element is not added to the array.
- (void) sortIntoRegions: tempList
{
for (beans *arrayElement in tempList) {
NSLog(#"region: %#", arrayElement.region);
if ([arrayElement.region isEqualToString:#"Africa"]) {
[_africa addObject:arrayElement.name];
} else if ([arrayElement.region isEqualToString: #"Asia & South Pacific"]) {
[_asia addObject:arrayElement.name];
}
else if ([arrayElement.region isEqualToString: #"Central America"]) {
[_centralAmerica addObject:arrayElement];
} else if ([arrayElement.region isEqualToString: #"South America"]) {
[_southAmerica addObject:arrayElement];
}
}
}
I'm going to go out on a limb and guess that you have declared _africa, _asia, _centralAmerica, and _southAmerica to be NSMutableArray *, but you have not initialized them. That is, you have not written any code like this:
_africa = [[NSMutableArray alloc] init];
_asia = [[NSMutableArray alloc] init];
_centralAmerica = [[NSMutableArray alloc] init];
_southAmerica = [[NSMutableArray alloc] init];
Your instance variables are set to nil when your object is created. Sending a message (like addObject:) to nil has no effect (and doesn't print a warning or error message).