NSMutableDictionary - entries appear to be over-written by each addition - objective-c

I'm fairly new to Objective-C; but have been coding for years and this one really stumps me.
I'm trying to build an iPhone app and wanted to create a "settings" screen which will use a Table format. (Xcode 5.1.1).
I want to future proof the main Settings screen and make it easy for the application coding by hiding the "hard work" in subroutines/methods.
I may be getting too clever but I've created a class for each 'setting' that contains screen prompts, default values etc and using an Enum to cross-reference it (so the compiler will highlight typos etc)
The problem I'm encountering is that when I add entries to my NSMutableDictionary and use lldb to print the values; every entry seems to have the same "key" and values. I've tried converting the eNum to an NSNumber and also as an NSString -- no difference in the result - so I'm obviously doing something else daft but just can't see it
The following code is from various .m & .h files, I've omitted boring stuff that you always "have to have" to keep it short
// basic x-ref I want to use in my code
typedef NS_OPTIONS(NSInteger, ConfigurationType) {
unDefined = -1,
Server = 0,
Id = 1,
Phone = 2
};
// definition for a "single" Settings value
#interface SettingDefinition : NSObject
#end
#implementation SettingDefinition
ConfigurationType _cfgType;
NSString *_cfgName;
NSString *_screenTitle;
NSString *_value;
- (NSString *)description
{
NSString *className = NSStringFromClass([self class]);
return [NSString stringWithFormat:#"<%#: x%p Type=%d dbKey=%# '%#' -> %#>", className, self, _cfgType, _cfgName, _screenTitle, _value];
}
- (id)initType:(ConfigurationType)cfgOption
withDbKey: (NSString*)dbKey
asOptionTitle:(NSString*)cfgTitle
withValue:(NSString*)itmValue
{
self = [super init];
if (self) {
_screenTitle = cfgTitle;
_cfgName = dbKey;
_cfgType = cfgOption;
_value = itmValue;
}
return self;
}
#end
#interface Configuration : NSObject
#end
#implementation Configuration {
NSMutableDictionary *Settings; // List of Setting structures
};
- (id)init {
self = [super init];
if (self) {
Settings = [[NSMutableDictionary alloc]init];
[self add:Server withDbKey:#"Server" asOptionTitle:#"Server"];
[self add:Id withDbKey:#"Id" asOptionTitle:#"Your ID"];
[self add:Phone withDbKey:#"Phone" asOptionTitle:#"Phone No."];
}
return self;
}
- (void) add:(ConfigurationType)cfgOption
withDbKey:(NSString*)dbKey
asOptionTitle:(NSString*)cfgTitle
{
NSString * itmValue = [self configurationValue: cfgOption cfgName:dbKey];
SettingDefinition *x = [[SettingDefinition alloc]
initType: cfgOption
withDbKey: dbKey
asOptionTitle: cfgTitle
withValue: itmValue];
[Settings setObject:x forKey:[self asKey:cfgOption]];
}
- (NSString *) asKey:(ConfigurationType) settingType {
NSString *rc = [NSString stringWithFormat:#"%d", settingType];
return rc;
}
- (NSString *) configurationValue:(ConfigurationType) settingType {
// returns a suitable value from my system setup
// which is initially a null value until the user sets everything up
}
the debug window shows the following when I break after the final call to [self add: ...]
(lldb) po Settings
{
0 = "<SettingDefinition: x0x8e7c280 Type=2 dbKey=Phone 'Phone No.' -> (null)>";
1 = "<SettingDefinition: x0x8c703a0 Type=2 dbKey=Phone 'Phone No.' -> (null)>";
2 = "<SettingDefinition: x0x8e7c310 Type=2 dbKey=Phone 'Phone No.' -> (null)>";
}
The (null) is obviously due to no data in 'value' yet; but why do they all show as 'Phone'; if I break after the second call to [self add:..] they all show as 'Id'
UPDATE:
DOH! obviously they're globals (I've been using another IDE where everything is local until exposed) .. If I enclose them in braces in the implementation as the documentation states then the exhibited problem vanishes. I have properties to access the variables but as the setter does more than just set the memory, I thought I'd need my "own" variables to hold the data.. said it was something daft .. thank you!

Related

Arc Semantic Issue: No visible #interface for 'CSRGaia' declares the selector 'getUserEQ:'

I'm working on a react-native project that requires some native modules. One of them is a Bluetooth module that allows me to access some CSRGaia methods. Ultimately, I want to be able to read the eq values on the PS-key so that I can set my equalizer to the corresponding values. I know almost nothing about Objective-C
Currently there is a method that looks like this:
RCT_EXPORT_METHOD(setEQValues:(NSArray *)values callback:(RCTResponseSenderBlock)callback)
{
CSRPeripheral *connectedPeripheral = [CSRConnectionManager sharedInstance].connectedPeripheral;
if( connectedPeripheral == nil )
{
callback(#[DISCONNECTED]);
return;
}
[[CSRGaia sharedInstance] setEQValues:values];
}
This works with no issues. However, when I tried to write my own
RCT_EXPORT_METHOD(getUserEQ: (NSArray *)values callback:(RCTResponseSenderBlock)callback)
{
CSRPeripheral *connectedPeripheral = [CSRConnectionManager sharedInstance].connectedPeripheral;
if( connectedPeripheral == nil)
{
callback(#[DISCONNECTED]);
return;
}
[[CSRGaia sharedInstance] getUserEQ: values];
}
I get the following error:
No visible #interface for 'CSRGaia' declares the selector 'getUserEQ:'
I double checked the CSRGaia.m file to verify that both methods exist.
- (void)setEQValues:(NSArray *)values {
NSMutableData *payload = [[NSMutableData alloc] init];
for( NSNumber *value in values ) {
uint8_t hex = [value unsignedCharValue];
[payload appendBytes:&hex length:1];
}
[self sendCommand:GaiaCommand_SET_HEP_EQ_PSKEY
vendor:CSR_GAIA_VENDOR_ID
data:payload];
}
- (void)getUserEQ {
[self sendCommand:GaiaCommand_GetUserEQControl
vendor:CSR_GAIA_VENDOR_ID
data:nil];
}
you are calling this method:
'getUserEQ:'
notice the 2 dots colon
it's different from method
'getUser'
with no colon
and in your .m file there is only
- (void)getUserEQ {}
i guess you wanted to use the setter method, instead
- (void)setEQValues:(NSArray *)values{}
like this:
[[CSRGaia sharedInstance] setEQValues: values];
add anyway both
- (void)getUserEQ;
- (void)setEQValues:(NSArray *)values;
in CSRGaia.h file
between
#interface OSRGaia
and
#end

How to parse and take only this string value

I wanted to get only array string value app. As example(SLGoogleAuth ,HalfTunes,TheBackgrounder,Calculiator) . But don't know how to do?
It's a code.
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//
Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace");
SEL selector=NSSelectorFromString(#"defaultWorkspace");
NSObject* workspace = [LSApplicationWorkspace_class performSelector:selector];
SEL selectorALL = NSSelectorFromString(#"allApplications");
NSLog(#"apps: %#", [workspace performSelector:selectorALL]);
}
It's output:
Thanks in advance
You do not want to parse that. NSLog prints out a description of an object. You want to access that value directly.
[LSApplicationWorkspace allApplications];
returns NSArray of LSApplicationProxy. LSApplicationProxy class has a ivar _bundleURL that contains information that you need. You need runtime functions to access it. Working example below:
// #import <objc/runtime.h>
Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace");
SEL selector=NSSelectorFromString(#"defaultWorkspace");
NSObject* workspace = [LSApplicationWorkspace_class performSelector:selector];
SEL selectorALL = NSSelectorFromString(#"allApplications");
NSArray* appProxies = [workspace performSelector:selectorALL];
Ivar bundleUrlIvar = class_getInstanceVariable([appProxies.firstObject class], "_bundleURL");
NSMutableString* result = [NSMutableString string];
for (id appProxy in appProxies)
{
NSURL* url = object_getIvar(appProxy, bundleUrlIvar);
// at this point you have the information and you can do whatever you want with it
// I will make it a list as you asked
if (url)
{
[result appendFormat:#",%#", [url lastPathComponent]];
}
}
if (result.length > 0)
{
// remove comma from beginning of the list
[result deleteCharactersInRange:NSMakeRange(0, 1)];
}
NSLog(#"apps: %#", result);
Note that this will be rejected by AppStore as you are using private apis. So use at your own discretion.

Building 3 methods in Objective C

I'm having trouble with a school problem in Objective C. I need to build 3 methods. The first method tells you if someone is in a line. If nobody is in the line it tells you nobody is in the line otherwise it tells you who is in the line and it lists the names on a new line.
The second method adds names to the line.
The third method removes a name from the line and tells you who was removed.
First method:
-(NSString*)stringWithDeliLine:(NSArray*) deliLine{
NSString *empty = #"The line is currently empty.";
//Some kind of formatted string
if(deliLine == nil || [deliLine count] == 0)
{
empty;
}
else
{
//formatted string
}
//not sure how to return either empty or formatted string
}
Second Method:
-(void)addName:toDeliLine:(NSString*)name:(NSMutableArray*)deliLine{
[deliLine addObject:name];
}
The third method I was going to use removeObject but the instructions said not to use it so I have no idea where to start.I have the signature I think.
-(NSString*)serveNextCustomerInDeliLine:(NSMutableArray*)deliLine{
return nil;
}
For the first method I'm not sure why my literal string won't work in the if statement. I thought I was saying look at the array if nothing is in the array then it's the first object and show the string literal. else show some kinda of formatted string. I've tried all kinds of strings but none seem to be working so that's why I have the comment formatted string. If someone could give me a hint that would be great. I don't need the answer just a clue on what to think about. This is long post sorry.
A possible implementation can be the following. Please note that I have not testes edge cases and I wrote the code without Xcode support
#import <Foundation/Foundation.h>
#interface Line : NSObject
- (NSString*)printLine;
- (void)addCustomer:(NSString*)customer;
- (NSString*)removeCustomer:(NSString*)customer;
#end
#import "Line.h"
#interface Line ()
#property (nonatomic, strong, readwrite) NSMutableArray<NSString*> *customers;
#end
#implementation Line
- (instancetype)init {
self = [super init];
if (self) {
_customers = [NSMutableArray array];
}
return self;
}
- (NSString*)printLine {
NSUInteger count = self.customers.count;
if(count == 0) {
return #"Empty";
}
NSMutableString *descr = [NSMutableString string];
for (NSString *customer in self.customers) {
[descr appendString:[NSString stringWithFormat:#"%# ", customer]];
}
return [descr copy];
}
- (void)addCustomer:(NSString*)customer {
[self.customers addObject:customer];
}
- (NSString*)removeCustomer:(NSString*)customer {
NSUInteger index = [self.customers indexOfObject:customer];
if(index == NSNotFound) {
return [NSString stringWithFormat:#"%# not removed", customer];
}
NSString *removedCustomer = [self.customers objectAtIndex:index];
[self.customers removeObjectAtIndex:index];
return removedCustomer;
}
#end
Usage:
Line *line = [[Line alloc] init];
[line addCustomer:#"customer"];
NSLog(#"%#", [line printLine]);
NSLog(#"%#", [line removeCustomer:#"customer"]);
NSLog(#"%#", [line printLine]);
Edit:
I've updated my answer, passing the array as a parameter is not necessary, just initialize deliLine as a mutable array property.
For you first method, you could do the following,
- (NSString *)deliLineContents {
NSString *empty = #"The line is currently empty.";
NSMutableString *namesInQueue = [[NSMutableString alloc] init];
if(self.deliLine == nil || [self.deliLine count] == 0) {
return empty;
} else {
// Loop through your array and return a string of all the names
for (NSString *string in self.deliLine ) {
[namesInQueue appendString:string];
}
}
return [NSString stringWithString:namesInQueue];
For your second method, you're already pretty much there, maybe look up how to construct method signatures.
- (void)addNameToDeliLine:(NSString*)name {
[self.deliLine addObject:name];
}
For your third method, not sure if this meets your requirement, if not let me know.
- (NSString *)customerRemovedFromLine {
// I've making an assumption that you want to remove the first customer
NSString *servedCustomer = [self.deliLine objectAtIndex:0];
[self.deliLine removeObjectAtIndex:0];
return servedCustomer;
}
You probably don't need to pass deliLine around, just create it as a property and access it with self.deliLine. Anyway hope this helps, good luck.

New in Objective C. Simple bug at runtime. Thread 1: breakpoint 1.1

I am learning Objective C and I have tried to override my superclass (NSObject) description method in my BNRItem class. Though it seems like I have done everything right, my NSLog does not seem to use my overridden description method. Instead I see a Thread 1 Breakpoint 1.1 in my definition of the description method and more precisely, where I defined the descriptionString.
Here is my console output.
2015-08-30 20:49:00.622 RandomItems[46034:1002101] Zero
2015-08-30 20:49:00.623 RandomItems[46034:1002101] One
2015-08-30 20:49:00.623 RandomItems[46034:1002101] Two
2015-08-30 20:49:00.623 RandomItems[46034:1002101] Three
(lldb)
My main.m file:
#import <Foundation/Foundation.h>
#import "BNRItem.h"
int main(int argc, const char * argv[]) {
#autoreleasepool {
// Create a mutable array object, store its address in items variable...
NSMutableArray *items = [[NSMutableArray alloc] init];
//Send the message addObject: to the NSMutableArray pointed
//by the variable item, passing a string each time
[items addObject:#"One"];
[items addObject: #"Two"];
[items addObject: #"Three"];
// Send another message, insertObject:atIndex;, to that same array object
[items insertObject:#"Zero" atIndex:0];
// For every item in the items array ...
for (NSString *item in items){
//Log the description of item
NSLog(#"%#", item);
}
// Create a BNRItem instance and log its instance variables in the console
BNRItem *item = [[BNRItem alloc] init];
// Set item name to Red Sofa
item.itemName = #"Red Sofa";
item.serialNumber= #"A1B2C";
item.valueInDollards = 100;
//
NSLog(#"%#", item);
//Destroy the mutable array object
items = nil ;
}
return 0;
}
My header file:
#import <Foundation/Foundation.h>
#interface BNRItem : NSObject
{
NSString *_itemName;
NSString *_serialNumber;
int _valueInDollards;
NSDate *_dateCreated;
}
- (void)setItemName:(NSString *)str;
- (NSString *)itemName;
- (void)setSerialNumber:(NSString *)str;
- (NSString *)serialNumber;
- (void)setValueInDollards:(int)v;
- (int)valueInDollards;
- (NSDate *)dateCreated;
#end
And finally, my implementation file:
#import "BNRItem.h"
#implementation BNRItem
- (void)setItemName:(NSString *)str
{
_itemName = str;
}
- (NSString *)itemName
{
return _itemName;
}
- (void)setSerialNumber:(NSString *)str
{
_serialNumber = str;
}
- (NSString *)serialNumber
{
return _serialNumber;
}
- ( void )setValueInDollards:(int)v
{
_valueInDollards = v;
}
- ( int )valueInDollards
{
return _valueInDollards;
}
-( NSDate * )dateCreated
{
return _dateCreated;
}
- ( NSString * )description
{
NSString *descriptionString = [[NSString alloc] initWithFormat:#"%# (%#): Worth %d, recorded on %#", self.itemName, self.serialNumber, self.valueInDollards, self.dateCreated];
return descriptionString;
}
#end
It sounds like you have simply set a breakpoint on a line in your -description method. The debugger is stopping your program at the breakpoint. There's no indication of an actual error.
If you hit the continue button in Xcode, your program would probably proceed just fine.
You can disable the breakpoint, tell Xcode to ignore all breakpoints, or delete the breakpoint if you don't want to break there. The breakpoint will look like a blue arrow in the margins to the left of your code. Right-click or Control-click on it to see options.
To make Xcode ignore breakpoints, toggle the breakpoint button in the debugging toolbar. It also looks like a right-pointing arrow. It will be filled in blue if breakpoints are enabled. It will be an outline if they're disabled.

Adding NSImage to IKimagerowserview

I am able to add NSImages to my NSCollectionView without having to first save them on disk. The images are fed into the collection view from an NSMutableArray. This way people can see the images without first having to save them.
Is there something similar that I can achieve with IKImageBrowserView? NSCollectionView is functional when it comes to representing images, but I would like to see if I can do something similar with IKImageBrowserView.
I can easily implement IKImageBrowserView with images saved on disk (Apple docs cover how this works) but can't figure out exactly where to look or how to go about adding images to the browser view directly from NSMutableArray instead of first saving them images to disk.
I'm at a loss here. Apart from the docs, I'm not really sure where else to look for direction. Or what to even call what I'm looking to do.
EDIT: (Here's some of the code)
// The data object -- if it is possible to represent an image object, this is where I am probably going wrong.
#interface ImageObject : NSObject
#property (readwrite, copy) NSImage *image;
#property (readwrite, copy) NSString *imageID;
- (id)initWithImage:(NSImage *)anImage;
- (NSString *)imageUID;
- (NSString *)imageRepresentationType;
- (id)imageRepresentation;
#end
#implementation ImageObject
#synthesize image = _image;
#synthesize imageID = _imageID;
- (id)initWithImage:(NSImage *)anImage
{
if (self = [super init]) {
_image = [anImage copy];
}
return self;
}
- (NSString *)imageUID
{
return _imageID;
}
- (NSString *)imageRepresentationType
{
return IKImageBrowserNSImageRepresentationType;
}
- (id)imageRepresentation
{
return _image;
}
#end
// This is how objects are supposed to be added to the browserView. All of this is straight from Apple.
- (void)updateDatasource
{
[_browserImages addObjectsFromArray:_importedImages];
[_importedImages removeAllObjects];
[imageBrowser reloadData];
}
- (NSUInteger)numberOfItemsInImageBrowser:(IKImageBrowserView *)aBrowser
{
return [_browserImages count];
}
- (id)imageBrowser:(IKImageBrowserView *)aBrowser itemAtIndex:(NSUInteger)index
{
return [_browserImages objectAtIndex:index];
}
This is where I try to add NSImages to the browserView but nothing happens. The array gets populated (which means the images are generated without any errors) but nothing happens on the screen.
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:[oPanel URL] options:nil];
NSMutableArray *timesArray = [self generateTimeForSpecifiedNumberOfFramesInVideo:10 UsingAsset:asset];
self.imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:asset];
[[self imageGenerator] generateCGImagesAsynchronouslyForTimes:timesArray completionHandler:^(CMTime requestedTime, CGImageRef image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError *error) {
NSImage *testImage = [[NSImage alloc] initWithCGImage:image size:NSZeroSize];
if (result == AVAssetImageGeneratorSucceeded) {
ImageObject *objects = [[ImageObject alloc] initWithImage:testImage];
[_importedImages addObject:objects];
}
}
As for exploring the rest of the search results...been there done that. If I did miss anything, kindly mark this question as duplicate indicating what post already existed where this issue has been addressed.
EDIT:
I have accepted the answer below. Along with the unique IDs problem. I had overlooked a simple thing which was the requirement to call the updateDatasource method.
The most important point of using IKImageBrowser is create a unique image ID for each element. The following is an example. In fact, it comes from the project that I'm currently working on. I have just implemented IKImageBrowser in it. The code below assumes that you have 36 images (Image01.png, Image02.png..., Image36.png) imported into the project.
// .h
#interface AppDelegate : NSObject {
IBOutlet IKImageBrowserView *browserView;
NSMutableArray *imageArray;
}
// .m
#import "IKBBrowserItem.h"
#import <QuartzCore/QuartzCore.h>
- (void)applicationWillFinishLaunching:(NSNotification *)notification {
imageArray = [[NSMutableArray alloc]init];
}
- (void)populateImage {
for (NSInteger i2 = 1; i2 <= 36 ; i2++) {
NSString *name;
if (i2 < 10) {
name = [NSString stringWithFormat:#"Image0%ld",(long)i2];
} else {
name = [NSString stringWithFormat:#"Image%ld",(long)i2];
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
NSImage *Image0 = [NSImage imageNamed:name];
NSInteger ran = [self genRandom:1000000:9999999];
NSString *imageID = [NSString stringWithFormat:#"%#%li",name,ran];
IKBBrowserItem *item = [[IKBBrowserItem alloc] initWithImage:Image0 imageID:imageID:name];
[imageArray addObject:item];
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
[browserView reloadData];
}
- (NSInteger)genRandom: (NSInteger)min :(NSInteger)max {
int num1;
do {
num1 = arc4random() % max;
} while (num1 < min);
return num1;
}
You don't need to use a random integer generator (genRandom) above, but just make sure that no imageID is the same.
This web site has a sample project, which should get you going. (I have no affiliation.) So make sure you download and run it. Then take a closer look and improve it for your needs.