h
#import <UIKit/UIKit.h>
#interface LoginViewController : UIViewController <UITextFieldDelegate>
#property (nonatomic, retain) NSDictionary *_data;
#end
m
#import "LoginViewController.h"
#import "XMLReader.h"
#implementation LoginViewController
static NSDictionary *_raceInformation;
#synthesize _data, bibInput, lastNameInput, error;
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
NSError *e = [NSError alloc];
NSString *xml = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"testhalfmarathon" ofType:#"xml"] encoding:NSUTF8StringEncoding error:&e];
NSDictionary *asdf = [XMLReader dictionaryForXMLString:xml error:nil];
self._data = [XMLReader dictionaryForXMLString:xml error:nil];
//[xml release];
//[e release];
// !! BREAKPOINT HERE
}
return self;
}
When I hit the breakpoint, the value for self._data is nil. However, the value for asdf is the correct dictionary value I would expect in self._data. What gives?
backstory: I'm a n00b when it comes to MRC as I usually use ARC/GC languages.
What line of code did you put the breakpoint against? If it was just a blank line it will actually break at the previous valid line of code, which may have been before self._data was set.
Try putting NSLog(#"data %#", self._data); instead of your breakpoint and see what gets logged.
BTW, I see you had [xml release], which you commented out, presumably because it wasn't working. The reason this line is wrong is that [XMLReader dictionaryForXMLString...] returns an autoreleased object that shouldn't be released again.
In general, in Objective-C if a method name doesn't begin with "new", "alloc" or "copy" then it returns an autoreleased object that you don't need to release yourself.
Related
I have an interface that looks like this: #import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
NS_ASSUME_NONNULL_BEGIN
#interface AVBase : NSObject
#property NSString *portName;
#property NSString *uid;
#property NSString* port;
- (id) initWithPortName:(NSString *)portName andUID:(NSString *)uid andPort:(AVAudioSessionPort)port;
#end
NS_ASSUME_NONNULL_END
and the .m file
#implementation AVBase
- (id)initWithPortName:(NSString *)portName andUID:(NSString *)uid andPort:(AVAudioSessionPort)port
{
self = [super init];
if (self)
{
self.portName = portName;
self.uid = uid;
self.port = [port description];
}
return self;
}
#end
I want to create an array of current outputs for the AVAudioSession, so I do it like this:
NSMutableArray *myArray = [[NSMutableArray alloc] init];
AVAudioSession *session = AVAudioSession.sharedInstance;
NSArray *outputs = [[session currentRoute] outputs];
for(AVAudioSessionPortDescription* output in outputs)
{
AVBase* av = [AVBase alloc];
av = [av initWithPortNumber:output.portName andUID:output.UID andPort:output.portType];
[myArray addObject:av];
}
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:myArray options:NSJSONWritingPrettyPrinted error:&error];
But when I try to serialize myArray I get an error that says:
Exception 'Invalid type in JSON write (AVBase)
I don't understand what's wrong, all the properties in my class are of type NSString so it should work.
NSJSONSerialization accepts only NSArray, NSDictionary, NSString, NSNumber (and NSNull), for its top level, but all sublevels/subproperties too.
myArray is a NSArray of AVBase, and AVBase isn't one of them.
You need to convert an AVBase into a NSDictionary first.
-(NSDictionary *)toDict {
return #{#"portName": portName, #"uid": uid, #"port": port};
}
Then:
[myArray addObject:[av toDict]];
If you don't use AVBase, or just for it, you can construct the NSDictionary directly from AVAudioSessionPortDescription *output, no need to use the AVBase here.
I am using Afnetworking version 3.0 and trying to load some data. The data is coming in the response object properly and I am trying to parse that in an NSMutableArray. However this is crashing giving me error-
"warning: could not load any Objective-C class information. This will significantly reduce the quality of type information available."
My Data Parsing class is following-
DataProcessing.h-
#import <Foundation/Foundation.h>
#import "Book.h"
#import <AFNetworking/AFHTTPSessionManager.h>
#import <MBProgressHUD/MBProgressHUD.h>
#interface DataProcessing : NSObject
-(void)getAllTheBooks;
-(void)setBookList:(NSMutableArray*)list;
-(NSMutableArray*)getBookList;
#end
DataProcessing.m:
#import "DataProcessing.h"
#interface DataProcessing(){
Book *individualBook;
}
#property(nonatomic, strong) NSMutableArray *bookList;
#property(nonatomic, strong) BookDetails *bookDetails;
#end
#implementation DataProcessing
-(void)getAllTheBooks{
NSMutableArray *bookList =[[NSMutableArray alloc]init];
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:#"https://natasha....../items" parameters:nil progress:nil success:^(NSURLSessionTask *task, id responseObject) {
NSArray *bookCollectionArray = (NSArray*)responseObject;
for (NSDictionary *dict in bookCollectionArray) {
individualBook = [[Book alloc]init];
individualBook.bookID = [dict objectForKey:#"id"];
individualBook.bookLink = [dict objectForKey:#"link"];
individualBook.bookTitle = [dict objectForKey:#"title"];
[bookList addObject:individualBook];
}
[self setBookList:bookList];
} failure:^(NSURLSessionTask *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
-(void)setBookList:(NSMutableArray*)list{
self.bookList = [[NSMutableArray alloc]init];
self.bookList = list;
}
-(NSMutableArray*)getBookList{
return self.bookList;
}
#end
My Controller's viewDidLoad is as following-
- (void)viewDidLoad {
[super viewDidLoad];
DataProcessing *processing = [[DataProcessing alloc]init];
[processing getAllTheBooks];
NSMutableArray *array = [processing getBookList];
NSLog(#"%#", array);
}
When it crashes, it shows something like this -
Can anyone please help?
Hmm, I'm not 100% sure this would work but you could try initializing the mutable array in an init method, then just have the self.bookList = list; inside the setter method.
There's no need to allocate a new NSMutableArray inside of the bookList property's setter method, assign it to the bookList property, and then set it to the list being passed in. Instead of accessing the bookList property using self inside of the accessor methods it is advised to use the backing variable or ivar (instance variable) instead, which is prefixed by an underscore (e.g. _bookList) and known as direct access.
The reason you should use direct access instead of self.bookList is because self.bookList = [[NSMutableArray alloc] init] is translated to [self setBookList:[[NSMutableArray alloc] init]];, which means that inside of your setter method for the bookList property you will be calling the setter method again and causing a recursive loop. For example, the code you have as of now will translate in to something like the following:
-(void)setBookList:(NSMutableArray*)list{
[_bookList release];
[self setBookList:[[NSMutableArray alloc]init]];
[self setBookList:list];
[_bookList retain];
}
Thus, on the second call to setBookList:, you're trying to release the bookList property after you have already released it, which is why you're getting the EXC_BAD_ACCESS error. If you're not doing any type of validation or in need of custom code inside of the setter method you do not have to define it as the setter method will be automatically defined for you. However, for reference, below is the proper way to define your setter method without any modification (in compliance with the aforementioned and ARC):
- (void)setBookList:(NSMutableArray *)bookList
{
if (_bookList != bookList)
_bookList = bookList;
}
I have a class called TAPJSONPoster. It's .h is as follows:
#import <Foundation/Foundation.h>
#interface TAPJsonPoster : NSObject
-(id)initWithURL:(NSURL*)url WithJson:(NSData*)jsondata;
-(NSData*)getResponse;
#end
It's .m is:
#import "TAPJsonPoster.h"
#interface TAPJsonPoster()
#property NSURL *url;
#property NSData *jsondata;
#end
#implementation TAPJsonPoster
-(id)initWithURL:(NSURL*)url WithJson:(NSData*)jsondata
{
self=[super init];
self.url=url;
self.jsondata=jsondata;
return self;
}
-(NSData*)getResponse
{
return self.jsondata;
}
#end
I still have tto fill in getResponse, but the init itself is not working. In my ViewController I have
#import "TAPJSONPostConnector.h"
and a method to login:
- (IBAction)loginValidate:(id)sender {
NSString *username=self.unTextField.text;
NSString *password=self.pwdTextField.text;
NSArray *params=[NSArray arrayWithObjects:#"userId",#"password", nil];
NSDictionary *dictionary=[NSDictionary dictionaryWithObjectsAndKeys:
#"_requestName", #"login",
#"_ParamNames", params,
#"userId", username,
#"password", password,
nil];
NSData *data=[NSJSONSerialization dataWithJSONObject:dictionary options:NSJSONWritingPrettyPrinted error:nil];
NSURL *url=[NSURL URLWithString:#"loginURL"];
TAPJSONPostConnector *connector=[[TAPJSONPostConnector alloc] initWithURL:url WithJson:data];
}
The last line where I am making the PostConnector is giving me an error saying that
No #interface in TAPJSONPostConnector declares the selector initWithURL:WithJson
What am I doing wrong?
EDIT
I put in [connector getResponse] below the connector initialization and I get the same error for this method also, am I doin something wrong in importing?
Well, you're calling the initWithURL:withJson: initializer on TAPJSONPostConnector
TAPJSONPostConnector *connector=[[TAPJSONPostConnector alloc] initWithURL:url WithJson:data];
But it looks like this is declared on the TAPJsonPoster class. Perhaps this is what you meant.
TAPJsonPoster *connector=[[TAPJsonPoster alloc] initWithURL:url WithJson:data];
Either you're allocating a wrong object TAPJSONPostConnector instead of TAPJsonPoster
TAPJsonPoster *connector=[[TAPJsonPoster alloc] initWithURL:url WithJson:data];
Or you forgot to change the super class for TAPJSONPostConnector as TAPJsonPoster
#interface TAPJSONPostConnector : TAPJsonPoster
Change whatever fits your needs
One tip if you can change the name of method initWithURL: withJson: that will be according to the naming convetion
I found this implementation of a hash table written in objective-c. I can follow almost all of it, but am struggling to understand how exactly the -(id) init function works. This is the method in the HashTable.m file with 3 lines (I repasted it below right after the question). Could someone explain what exactly it is doing? I included some of the other relevant code although for the most part I think I can follow the rest. Despite that I'm unclear as to the specifics of the init method. Thanks
-(id)init
{
self =[super init];
self.othercontainer = [[NSMutableArray alloc]init];
return self;
}
HashTable.h
#import <Foundation/Foundation.h>
#interface HashTable : NSObject
#property(nonatomic) NSMutableArray* othercontainer;
-(id)objectForKey:(NSString*)name;
-(void)setObject:(id)object forKey:(NSString*)name;
-(id)init;
#end
HashTable.m
#import "HashTable.h"
#import "Person.h"
#implementation HashTable
-(id)init
{
self =[super init];
self.othercontainer = [[NSMutableArray alloc]init];
return self;
}
-(id)objectForKey:(NSString*)name
{
Person* tempPerson = nil;
for (id item in self.othercontainer)
{
NSString* tempName = [((Person*)item) name];
if ([tempName isEqualToString:name])
{
tempPerson = item;
break;
}
}
return tempPerson;
}
-(void)setObject:(id)object forKey:(NSString*)name
{
[self.othercontainer addObject:object];
}
#end
Part of ViewController.m
NSData *data;
NSFileHandle *fh;
NSString *inBoundFile = #"/Users/user/Desktop/names.txt";
NSString *fileString;
fh = [NSFileHandle fileHandleForReadingAtPath:inBoundFile];
data = [fh readDataToEndOfFile];
fileString = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSArray *PersonArray = [fileString componentsSeparatedByString:#"\n"];
self.container = [[HashTable alloc]init];
for (int x= 0; PersonArray.count > x ;x++) {
NSArray* tempNameandAddress = [PersonArray[x] componentsSeparatedByString:#" "];
Person *personA = [[Person alloc]init]; //could be other ways of defining an instance of an object
personA.name = tempNameandAddress[0];
personA.address = tempNameandAddress[1];
if ([self.container objectForKey:personA.name] == nil)
[self.container setObject:personA forKey:personA.name];
else
NSLog(#"%# already exists \n",personA.name);
}
This is simply an almost right common init.
self is set to the object returned by the superclass init.
Then they miss one proper step.
The next step should be if (self) { ...additional setup... }
Basically only creating ivars/properties if self as returned from super init is not nil.
If self is nil at that point you would normally just bypass additional code and go straight to return self. (Returning nil)
The next line is just creating the NSMutableArray ivar for the othercontainer property.
This is also not quite right.
In init, this is when you should use the synthesized ivar directly.
_othercontainer = [[NSMutableArray alloc] init];
Nothing special here.
I can pass basic data between classes, but when I try to pass a NSString* from my UIApplicationDelegate I get an EXC_BAD_ACCESS / NSZombie.
Is there something special I have to do to return an NSObject? Does this have to do with threading? (I thought the atomic setting on the property would take care of that?)
AppDelegate.h:
#interface AppDelegate : NSObject <UIApplicationDelegate> {
NSString * currentNoteName;
}
#property (atomic, assign) NSString *currentNoteName;
#end
AppDelegate.m:
- (void)timerCallback:(NSTimer *)timer {
currentNoteName = [NSString stringWithCString:(tone->freq).c_str() encoding:NSUTF8StringEncoding];
// This works:
NSLog(#"Current Note Name in timerCallback: %#", currentNoteName);
OtherObject.m:
// Returns a Zombie object & EXC_BAD_ACCESS:
NSString *currentNoteName = [appDelegate currentNoteName];
If not using ARC, you must using retain property:
#property (atomic, retain) NSString *currentNoteName;
and assign a value for it, using setter:
self.currentNoteName = [NSString stringWithCString: ...];
and don't forget to release instance of this ivar in your dealloc implementation of AppDelegate:
- (void) dealloc {
[currentNoteName release], currentNoteName = nil;
[super dealloc];
}
you are assigning a value and autoreleasing the NSString instance. Use retain instead.
The probleis is "assign", because the string from " [NSString stringWithCString" is auto-released.
Maybe u can change it to "copy" or "retain". (i think copy is better).