I'm using this very simple code from the Apple Guide:
NSMutableData *receivedData;
// Create the request.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.apple.com/"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
receivedData = [[NSMutableData data] retain];
} else {
// Inform the user that the connection failed.
}
But for the line receivedData = [[NSMutableData data] retain]; Xcode gives me an error: PushController.m:72:25: ARC forbids explicit message send of 'retain'
How to deal with it? I'm using the Xcode 4.4.1
You are currently using the ARC to reference count for you. (ARC is "Automatic Reference Counting", a new feature to iOS 5). Therefore you do not need to manually retain or release. You can either remove your retain calls all together or turn off ARC by doing the following:
Click on the name of the project on the navigation view in the left side, go to Targets -> Build Phases and add -fno-objc-arc to the "compiler flags" for any relevant files.
See here for info on removing.
See here for basic info on ARC.
I solved the problem as below. The code is for Objective-C.
Whichever file you wrote a method for getting images from CIImage to CGImageRef:
CGImageRef cgImage = [_ciContext createCGImage:currentImage fromRect:[currentImage extent]];
make that file as non ARC. Go to Project -> BuildPhase -> ComplieSources -> Your File -> add "-fno-objc-arc" to your file.
If you have .pch file in your project, make the following line comment:
#if !__has_feature(objc_arc)
#error This file must be compiled with ARC.
#endif
Go to the method which is used for creating images using the following function:
CGImageRef cgImage = [_ciContext createCGImage:currentImage fromRect:[currentImage extent]];
Declare _ciContext like this:
In the .h file, declare:
#property (strong, nonatomic) CIContext* ciContext;
In your method, create the context:
EAGLContext *myEAGLContext = [[EAGLContext alloc]
initWithAPI:kEAGLRenderingAPIOpenGLES2];
_ciContext = [CIContext contextWithEAGLContext:myEAGLContext options:nil];
Use the _ciContext for creating images.
Write the following method in the same file:
-(void)dealloc
{
[super dealloc];
[EAGLContext setCurrentContext:nil];
}
Turning ARC on or off is a project level setting, if you need to have code that works in both modes you need to use
#if __has_feature(objc_arc)
//dont do a release or a retain or autorelease
#else
//do the release
#endif
Related
I have created an AVAudioUnitSampler, but after inspecting memory via the Instruments tool, it appears that when the scene is changed, even though the AVAudioUnitSampler object is nil, the resources it initially loaded the first time around are still in memory. When I recreate the sampler, it reloads the resource and now I have double the amount of memory used for the sampler. How can I force the resources to deallocate?
Here's the code:
-(void) loadSampler {
// Instatiate audio engine
_engine = [[AVAudioEngine alloc] init];
_mixer = [_engine mainMixerNode];
_sampler = [[AVAudioUnitSampler alloc] init];
[self loadSoundFontInstrument]; //sound font is the instrument that the sampler will use to play sounds
[self makeEngineConnections];
[self startEngine];
}
-(void) loadSoundFontInstrument {
if (_sampler != nil) {
NSString* instrument = [[GameData sharedGameData].settings valueForKey:#"instrument"]; //decides on what sound font file to use
NSURL *piano = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:instrument ofType:#"sf2"]];
[_sampler loadSoundBankInstrumentAtURL:piano program:0 bankMSB:0x79 bankLSB:0 error:nil];
}
else
NSLog(#"ERROR: Sampler has not been initialized");
}
-(void)makeEngineConnections {
[_engine attachNode:_sampler];
[_engine connect:_sampler to:_mixer format:[_sampler outputFormatForBus:0]];
}
-(void)startEngine {
[_engine startAndReturnError:nil];
}
I solved the issue, but I'm not entirely sure why my solution solved it. Upon looking at the Leaks instrument, I noticed that the retain count for the _sampler variable was 2. The class (that you cannot see called MIDIController owns the _sampler object. The _engine object also retains a reference to the sampler. Even when I make my midi controller object nil, the sampler still remains in memory with a retain count of 1. The weird thing is that there is no longer any reference to the _engine object since it's parent has been deallocated so I'm not sure why it still persists.
TLDR: In short, I made _sampler, _engine and _mixer nil and that solved it.
I'm trying to write a simple (toy) program that uses the NSFilePresenter and NSFileCoordinator methods to watch a file for changes.
The program consists of a text view that loads a (hardcoded) text file and a button that will save the file with any changes. The idea is that I have two instances running and saving in one instance will cause the other instance to reload the changed file.
Loading and saving the file works fine but the NSFilePresenter methods are never called. It is all based around a class called FileManager which implements the NSFilePresenter protocol. The code is as follows:
Interface:
#interface FileManager : NSObject <NSFilePresenter>
#property (unsafe_unretained) IBOutlet NSTextView *textView;
- (void) saveFile;
- (void) reloadFile;
#end
Implementation:
#implementation FileManager
{
NSOperationQueue* queue;
NSURL* fileURL;
}
- (id) init {
self = [super init];
if (self) {
self->queue = [NSOperationQueue new];
self->fileURL = [NSURL URLWithString:#"/Users/Jonathan/file.txt"];
[NSFileCoordinator addFilePresenter:self];
}
return self;
}
- (NSURL*) presentedItemURL {
NSLog(#"presentedItemURL");
return self->fileURL;
}
- (NSOperationQueue*) presentedItemOperationQueue {
NSLog(#"presentedItemOperationQueue");
return self->queue;
}
- (void) saveFile {
NSFileCoordinator* coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:self];
NSError* error;
[coordinator coordinateWritingItemAtURL:self->fileURL options:NSFileCoordinatorWritingForMerging error:&error byAccessor:^(NSURL* url) {
NSString* content = [self.textView string];
[content writeToFile:[url path] atomically:YES encoding:NSUTF8StringEncoding error:NULL];
}];
}
- (void) reloadFile {
NSFileManager* fileManager = [NSFileManager defaultManager];
NSFileCoordinator* coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:self];
NSError* error;
__block NSData* content;
[coordinator coordinateReadingItemAtURL:self->fileURL options:0 error:&error byAccessor:^(NSURL* url) {
if ([fileManager fileExistsAtPath:[url path]]) {
content = [fileManager contentsAtPath:[url path]];
}
}];
dispatch_async(dispatch_get_main_queue(), ^{
[self.textView setString:[[NSString alloc] initWithData:content encoding:NSUTF8StringEncoding]];
});
}
// After this I implement *every* method in the NSFilePresenter protocol. Each one
// simply logs its method name (so I can see it has been called) and calls reloadFile
// (not the correct implementation for all of them I know, but good enough for now).
#end
Note, reloadFile is called in applicationDidFinishLaunching and saveFile gets called every time the save button is click (via the app delegate).
The only NSFilePresenter method that ever gets called (going by the logs) is presentedItemURL (which gets called four times when the program starts and loads the file and three times whenever save is clicked. Clicking save in a second instance has no noticeable effect on the first instance.
Can anyone tell me what I'm doing wrong here?
I was struggling with this exact issue for quite a while. For me, the only method that would be called was -presentedSubitemDidChangeAtURL: (I was monitoring a directory rather than a file). I opened a technical support issue with Apple, and their response was that this is a bug, and the only thing we can do right now is to do everything through -presentedSubitemDidChangeAtURL: if you're monitoring a directory. Not sure what can be done when monitoring a file.
I would encourage anyone encountering this issue to file a bug (https://bugreport.apple.com) to encourage Apple to get this problem fixed as soon as possible.
(I realize that this is an old question, but... :) )
First of all, I notice you don't have [NSFileCoordinator removeFilePresenter:self]; anywhere (it should be in dealloc).
Secondly, you wrote:
// After this I implement *every* method in the NSFilePresenter protocol. Each one
// simply logs its method name (so I can see it has been called) and calls reloadFile
// (not the correct implementation for all of them I know, but good enough for now).
You're right: it's the incorrect implementation! And you're wrong: it's not good enough, because it's essential for methods like accommodatePresentedItemDeletionWithCompletionHandler: which take a completion block as a parameter, that you actually call this completion block whenever you implement them, e.g.
- (void) savePresentedItemChangesWithCompletionHandler:(void (^)(NSError * _Nullable))completionHandler
{
// implement your save routine here, but only if you need to!
if ( dataHasChanged ) [self save]; // <-- meta code
//
NSError * err = nil; // <-- = no error, in this simple implementation
completionHandler(err); // <-- essential!
}
I don't know whether this is the reason your protocol methods are not being called, but it's certainly a place to start. Well, assuming you haven't already worked out what was wrong in the past three years! :-)
I'm uploading to Amazon S3 using the iOS SDK which is working great but I want to be able to trigger a method when the load is completed.
Here is my code:
AmazonS3Client *s3 = [[[AmazonS3Client alloc] initWithAccessKey:ACCESS_KEY_ID withSecretKey:SECRET_KEY] autorelease];
// Create the picture bucket.
[s3 createBucket:[[[S3CreateBucketRequest alloc] initWithName:[Constants pictureBucket]] autorelease]];
NSString *picName = [NSString stringWithFormat:#"%#%d", PICTURE_NAME, counter];
// Upload image data. Remember to set the content type.
S3PutObjectRequest *por = [[[S3PutObjectRequest alloc] initWithKey:picName inBucket:[Constants pictureBucket]] autorelease];
NSLog(#"------------ SUBMITTING img :%#", picName);
por.contentType = #"image/jpeg";
por.data = imageData;
counter++;
// Put the image data into the specified s3 bucket and object.
[s3 putObject:por];
Any help much appreciated thanks!
From the Amazon SDK Docs it seems that you get an S3PutObjectResponse
so
S3PutObjectResponse *response = [s3 putObject:por];
if ([response isFinishedLoading]) {
//do something
}
or maybe you are searching for connectionDidFinishLoading: which is a delegate method from NSURLConnection which it seem they use accordingly to AmazonServiceResponse Class Reference
in you .h file declare that you conform to the delegate protocol of NSURLConnection
#interface MyClass : NSObject <NSURLConnectionDelegate>
in your .m file implement the delegate methods you want
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
//do your stuff here
}
and tell the NSURLConnection that you handle the delegate methods in your .m file
S3PutObjectRequest *por = [[[S3PutObjectRequest alloc] initWithKey:picName inBucket:[Constants pictureBucket]] autorelease];
por.urlRequest.delegate = self; // this is important !!!
In general you should get used to work with delegates since they are often used throug the hole iOS SDK !!
You can find additional docu here: Delegates and Data Sources
I just have one more thing to add to the comments ( I know I am stepping out of conduct here but rep prevents me from commenting). I run these two lines just to be safe, as I have found the first does not retain its value consistently:
por.delegate = self;
[por setDelegate:self];
As you are a newbie like me, delegates are essentially handlers where the object looks when it calls obligatory methods that are sometimes required or not. If you set a delegate to self, it means that the putObjectRequest will reference obligatory methods on self when they are called, such as the method in Pfitz's answer. For UITableView, an example of a delegate method is (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath where the object, UITableView will reference self to look for a method cellForRowAtIndexPath in order to populate its object's cell queue.
REVISED...
The crux of the app is communicating with a database server. Responses from the server to the app are all in XML. There are several screens. Example, screen 1 lists the user's information, screen 2 lists the user's past trades, allows new trades, and so on.
Here is some code from my AppDelegate:
StartViewController *svc = [[StartViewController alloc] init];
TradeViewController *tvc = [[TradeViewController alloc] init];
CashViewController *cvc = [[CashViewController alloc] init];
ComViewController *covc = [[ComViewController alloc] init];
PrefsViewController *pvc = [[PrefsViewController alloc] init];
NSMutableArray *tabBarViewControllers = [[NSMutableArray alloc] initWithCapacity:5];
UITabBarController *tabBarController = [[UITabBarController alloc] init];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:svc];
[tabBarViewControllers addObject:navigationController];
navigationController = nil;
navigationController = [[UINavigationController alloc] initWithRootViewController:tvc];
[tabBarViewControllers addObject:navigationController];
navigationController = nil;
navigationController = [[UINavigationController alloc] initWithRootViewController:cvc];
[tabBarViewControllers addObject:navigationController];
navigationController = nil;
navigationController = [[UINavigationController alloc] initWithRootViewController:covc];
[tabBarViewControllers addObject:navigationController];
navigationController = nil;
navigationController = [[UINavigationController alloc] initWithRootViewController:pvc];
[tabBarViewControllers addObject:navigationController];
navigationController = nil;
[tabBarController setViewControllers:tabBarViewControllers];
[[self window] setRootViewController:tabBarController];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
Trying to stick with the MVC style, I have a singleton class which does all of the "processing".
Now an example on how I run into a wall… the user can change their email address on screen 5. Enter new email address into text field and click the save button. The button then calls a method from the singleton class which sends the new email address to the server and (via the URL) and receives a XML response confirming the change.
Here are my problems:
1. I start the spinner from the view controller before I make the singleton class method call - but not knowing when the app to server send/receive is finished, how do I make the spinner stop at the right time? I can't of it from the singleton class, I tried that. From what I know, it has to be from within the VC or is there a way to change VC output from my singleton class?
The singleton class NSURLConnection is handling ALL of my communication. Everything from a simple, email change all the way to updating transaction tables. This just seems wrong to me and makes it very difficult to keep track on who is calling what. Again, I am going by my interpretation of MVC. I think it would be much easier to have a NSURLConnection for every VC and do some processing in those classes. However that would not be MVC(ish).
I have close to a 100 variables, arrays, etc… in my singleton class which I use to assign values to all my VC. This also seems wrong to me but I can't think of any other way.
how can I distinguish in the NSURLConnection delegate
(connectionDidFinishLoading) which URL call is being made?
Each of the delegate methods (such as -connectionDidFinishLoading:) has a connection parameter that tells you which connection sent the message. A given connection can only load one URL at a time, so there's a one to one correspondence between URLs and connections.
How can I tell outside of "connectionDidFinishLoading" when the download is completed?
That method tells you when the connection is finished. It's up to you to store that information somewhere where it's useful to your app.
Update: Based on what you've added, your "processing" class is your app's model. The rest of the app shouldn't care that each transaction involves a message to the server -- that's the model's business alone. Also, there's no reason that the model has to be a single object (let alone a singleton) -- it can be a group of objects that work together.
So, you might have a class (let's call it Processor) that represents the application's interface to the model (some might even call this a "model controller"). An instance of Processor might create a local database for storing the current local state of the app.You might also have a Transaction class that represents a single transaction with the server. A transaction could create a request, send it to the server, get the response, update the database, and tell the Processor that the transaction is done. Or, maybe when some other part of the app (like one of your view controllers) asks the Processor to process a new transaction, the Processor passes the requesting object along to the transaction that it creates so that the transaction can update the requestor directly.
It's hard to say what the best plan for your app is without knowing where you're planning on taking it, but the usual guidelines hold:
break your problem into parts that are easier to solve
limit the scope of each class's responsibilities
if something seems to complicated, it probably is
Breaking your model up into several classes will make it easier to test, as well. You can imagine how easy it would be to write a set of unit tests for the Transaction class. The same goes for Processor -- if the server transaction stuff is in a different class, it's easier to test that the Processor is doing the right thing.
If you have multiple NSURLConnections for the same delegate, consider using a global (well, let's say rather an instance variable) NSMutableDictionary instance, in which you store the data depending on which NSURLConnection is being called. You can use, for example, the in-memory address of the connections converted to an NSString (something like
[NSString stringWithFormat:#"%p", connection]
should do the trick).
Also, in the connectionDidFinishLoading: and connection:didFailLoadWithError: methods, remove the keys corresponding to the NSURLConnections. Thus, you can tell it from 'outside' if a connection is finished: just check if it is in the dictionary or not.
If you're downloading any data over a network connection, I would suggest using ASIHttpRequest. This will allow you to download files asynchronously, meaning your interface doesn't freeze during the download process.
If you use ASIHttpRequest, you can also set the didFinishSelector. By doing this, you can control which method is called when a specific URL has finished loading.
Have a look at this:
NSURL *url = [NSURL URLWithString:#"http://allseeing-i.com"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request startAsynchronous];
[request setDidFinishSelector:#selector(requestDone:)];
Then:
- (void)requestDone:(ASIHTTPRequest *)request
{
// Use when fetching text data
NSString *responseString = [request responseString];
// Use when fetching binary data
NSData *responseData = [request responseData];
// If you want, you can get the url of the request like this
NSURL *url = [request url];
}
As for the second part of your question, if the requestDone: method has not been called, you know the download has not completed.
If you want to do something more complicated with multiple downloads, ASIHttpRequest offers queue functionality too. Take a look here.
Hope this will help you.
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
NSString *urlString = [[[connection originalRequest] URL] absoluteString];
if ([urlString caseInsensitiveCompare:#"http://www.apple.com"] == NSOrderedSame) {
//Do Task#1
}
else if ([urlString caseInsensitiveCompare:#"http://www.google.com"] == NSOrderedSame)
{
//Do Task#2
}
}
I would recommend subclassing NSURLConnection. Simply add two properties: an NSInteger, tag, and a BOOL, isFinished. This way, you can #define tags for each different request and then identify them by tag in your delegate methods. In connectionDidFinishLoading, you can set the isFinished BOOL to YES, and then you can check in other methods if then connection is finished.
Here's my own NSURLConnection subclass, TTURLConnection:
TTURLConnection.h:
#import <Foundation/Foundation.h>
#interface TTURLConnection : NSURLConnection <NSURLConnectionDelegate>
#property (nonatomic) NSInteger tag;
#property (nonatomic) BOOL isLocked;
- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:
(BOOL)startImmediately tag:(NSInteger)tagParam;
#end
TTURLConnection.m:
#import "TTURLConnection.h"
#implementation TTURLConnection
#synthesize tag;
- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:
(BOOL)startImmediately tag:(NSInteger)tagParam {
self = [super initWithRequest:request delegate:delegate
startImmediately:startImmediately];
if(self) {
self.tag = tagParam;
}
return self;
}
#end
I have a view controller that lists some data in an UITableView. To get the data downloaded I use ASIHTTPRequest which methods I have put in another class.
In my view controller I have setup the appropriate delegates to handle the data that is being retrieved from ASIHTTPRequest. So from my view controller in - viewDidLoad I alloc and init my class that holds the ASIHTTPRequest methods like so:
self.officesParser = [[[OfficesParser alloc] init] autorelease]; // retained property
Then in - viewDidAppear: I call [officesParser downloadOffices];
My - downloadOffices method looks like this:
- (void)downloadOffices {
// 1. Downloaded offices.json
NSURL *officesUrl = [NSURL URLWithString:#"http://example.com/example.json"];
ASIHTTPRequest *officesRequest = [ASIHTTPRequest requestWithURL:officesUrl];
// Always ask the server if there is new content available,
// If the request fails, use data from the cache even if it should have expired.
[officesRequest setCachePolicy:ASIAskServerIfModifiedCachePolicy|ASIFallbackToCacheIfLoadFailsCachePolicy];
// Store the cache permanently
[officesRequest setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy];
[officesRequest setTag:1];
OfficesViewController *vc = [[OfficesViewController alloc] init];
[officesRequest setDelegate:vc];
[vc release];
[officesRequest startAsynchronous];
}
Everytime after calling the [officesParser downloadOffices] method I get:
*** -[OfficesViewController respondsToSelector:]: message sent to deallocated instance 0x6a2f6c0
What am I doing wrong here?
You want vc to be delegate for officesRequest, however, after you allocate and initialize vc and set it to be the delegate, you immediately release it. Please note that delegate properties are usually assign, not retain. You are then responsible for keeping your delegate object in existence until no longer needed. So, if you plan to send messages to it in a near future, you can't immediately release it.