Tumgik
#they said to set a bool as true at the start of a project if establishing a variable in an if statement returns an error instead of a catch
just witnessed the very slight misteach thats going to create a huge error in some students first big corporate/govt job
0 notes
smsverse · 2 years
Text
Jeremy - Week of 10/21
This week has really been a LONG one. The work load of the Vertical Slice has caused us to work until midnight everyday during in person sessions. The worst part of it being that only one person can work in the main project at a time, so while they're doing that the other three members have to do side work that doesn't really move the main project forward. The very specific nature of our game intensifies these issues, as many of the interactions are one and done's. We put a lot of focus on getting models into the scene, but at the same time we needed to finalize all of our game mechanics.
I started the week off by implementing the second chunk of Building Swapping. This wasn't a very hard process, but it was fairly tedious as it took a bit to copy and paste some code.
Next up was a really big one. I created a small cutscene for when the Player travels on the Flying Creature. This seemed to be a very easy task, but as always with Unity there were complications. The most frustrating of them being the Flying Creature model disappearing when the Camera gets too close. This added a very tedious layer of checking to the cutscene, making sure that when I edited the shot the Flying Creature was still in frame. In the end though I was very proud of how this Cutscene came out, and it adds some real personality to the game.
Cutscene Timeline:
Tumblr media
While making the cutscene I had to add a few bits of code to teleport the Player, and the Flying Creature to the building roof. This was extremely easy because I used separate Cutscene models for the two of them, so the timing on the teleportation was extremely loose.
Along side this, I also coded more teleportation interactions for the game. One of them being the return trip from the Building with the Flying Creature. The other one has the Player move on top of a static building for another puzzle. All three of these use a variation of the same code. Though, the fade out effect I used seems to have decided to stop working OUT OF NOWHERE, but only on the Static Building Script.
Flying Creature Return Script:
Tumblr media
Flying Creature Cutscene and Return Sequence:
Static Building Transport Script:
Tumblr media
Alongside this I teamed up with Connor to tweak the Game State Manager Script. When Connor originally made it, the script used three Bools but now it works off of one Enum.
Game State Manager Enum:
Tumblr media
Also, instead of being run every frame it now only runs when the Universes are switched.
Updated Swapping Code (Now Runs with Universe Jump):
Tumblr media
We ran into a large bug that had to do with empty arrays in the inspector. This was remedied very easily, but the error came back again later. A script Jacob made for pick up items used the Destroy() command, which causes the arrays the items were once in to return null. After a tiny bit of research I discovered the 'continue' statement, which by passes the null reference.
Continue Conditions Script:
Tumblr media
I continued my work with Connor as we looked into the Car Pushing Mechanic. Originally, we were going for a purely Physics based approach with no scripting, but that turned out to be EXTREMELY inconsistent. So we switched to the ever reliable scripting method. Now the Cars use an Enum to define which direction the move in (either X or Z). The Cars have two triggers on them that run the script. This time they use a Bool to determine if they move in a negative direction. On Start() this will set a float multiplier to either 1 or -1 changing the Car's direction. All those words made it sound very very complex, but it was an extremely simple fix.
New Car Push Code:
Tumblr media
New Negative Car Push In Inspector Z:
Tumblr media
New Positive Car Push Inspector X:
Tumblr media
Back on my own I returned to the Static Building to finalize the puzzle the belongs on it. This one was very very simple. On the side of the Building sits a GameObject with a Rigidbody. Said Rigidbody has is Kinematic equals to true meaning they will float. On the press of a button, is Kinematic is turned off and the whole item drops to the floor.
Drop Off Static Building Code:
Tumblr media
Alongside all this solo work I assisted Connor and Jacob with some coding bug fixes.
While not working on the main project I designed a text box to use later on, when showing dialogue in game.
Text Box Image:
Tumblr media
I also wrote a few lines of Dialogue that would eventually go in the text box in a later sprint. I had some free time in the Morning on Wednesday, so I decide to record them.
Sample Voice Line:
Of course a Group A tradition is to recreate a Weezer album cover for each of our Milestones. Keeping with the trend of the last two we did another Color album. This time recreating Weezer's Green Album, which is where "Island in the Sun" one of their most popular songs originates from.
Original Album Cover:
Tumblr media
Original Non Edited Group A Photo:
Tumblr media
Final Edited Group A Green Album Cover:
Tumblr media
Change we made: In the original one of the band members hold a guitar. So we, as gamers, recreating this album decided to use a controller in place of the guitar. We have big plans for the next two album cover recreations so stay tuned and get excited!
0 notes
mlbors · 7 years
Text
Using the Repository Pattern with the Entity Framework
Through this article, we are going to see how to use the Repository Pattern with the Entity Framework in an ASP.NET MVC application.
Introduction
The Repository Pattern, as well as the Unit of Work Pattern, allows to create an abstraction layer between the data access layer and the business logic layer of an application. The purpose of creating this layer is to isolate data access layer so that the changes we may operate cannot affect the business logic layer directly. Implementing these patterns is also helpful for automated unit testing or test-driven development.
The Repository Pattern
The Repository Pattern allows us to create an abstraction layer between the data access layer and the business logic layer of an application. So, this Data Access Pattern offers a more loosely coupled approach to data access. So, we are able to create the data access logic in a separate class, called a Repository, which has the responsibility of persisting the application's business model.
The Unit of Work Pattern
The Unit of Work Pattern is a pattern that handles the transactions during data manipulation using the Repository Pattern. Unit of Work is referred to as a single transaction that involves multiple operations.
Setting up our project
First, we have to set up our project. So, let's head to Visual Studio and choose "New Project > Visual C# > Web > ASP.NET We Application (.NET Framework)". Name our application "GameApplication".
Now, let's install the Entity Framework and enable Migrations:
PM> Install-Package EntityFramework -Version 6.2.0 PM> enable-migrations
Installing Entity Framework and enabling migrations
Enable Routes attributes:
routes.MapMvcAttributeRoutes();
Adding Attributes Routes in AppStart/RouteConfig.cs file_
We can now create our first Model, that we are going to name "Game", like so:
namespace GameApplication.Models { public class Game { public int Id { get; set; } public string Name { get; set; } } }
Models/Game.cs file
We now have to create a "DAL" folder at the root of our application where we are going to create a "GameContext" file.
using System; using System.Collections.Generic; using System.Data.Entity; using System.Data.Entity.ModelConfiguration.Conventions; using System.Linq; using System.Web; using GameApplication.Models; namespace GameApplication.DAL { public class GameContext : DbContext { public GameContext() : base("GameContext") { } public DbSet<Game> Games { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); } } }
DAL/GameContext.cs file
Now, in the "Migrations/Configuration.cs" file, let's place the following code:
namespace GameApplication.Migrations { using System; using System.Collections.Generic; using System.Data.Entity; using System.Data.Entity.Migrations; using System.Linq; using GameApplication.Models; internal sealed class Configuration : DbMigrationsConfiguration<GameApplication.DAL.GameContext> { public Configuration() { AutomaticMigrationsEnabled = false; } protected override void Seed(GameApplication.DAL.GameContext context) { var games = new List<Game> { new Game{Name="Super Mario Bros"}, new Game{Name="Super Mario 64"}, new Game{Name="Super Mario Galaxy"} }; games.ForEach(e => context.Games.AddOrUpdate(p => p.Name, e)); context.SaveChanges(); } } }
Migrations/Configuration.cs file
We then have to make little changes in our "Web.config" file:
<connectionStrings> <add name="GameContext" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=GameApplication;Integrated Security=SSPI;" providerName="System.Data.SqlClient"/> </connectionStrings>
Web.config file edited
Now, we are ready to create our first Controller. Let's choose the "MVC 5 Controller with views, using Entity Framework" option and name it "GameController". We can enter "Game" as the Model class and "GameContext" for the database Context. After a few seconds, we can see that a Controller with some code in it and a bunch of Views have been created for us.
Now, if we run our application and go to "localhost:RANDOMPORT/game_", we should see something interesting.
Generic Repository
There are many ways to implement the Repository Patterns. We could create a Repository Class for each entity type, but it results in a lot of redundant code or in partial updates. So, to avoid this, we are going to create a Generic Repository.
First, let's create a file named "GenericRepository.cs" in our "DAL" folder and fill it like so:
using System; using System.Collections.Generic; using System.Data; using System.Data.Entity; using System.Linq; using System.Linq.Expressions; using System.Web; using GameApplication.Models; namespace GameApplication.DAL { public class GenericRepository<TEntity> where TEntity : class { internal GameContext context; internal DbSet<TEntity> dbSet; public GenericRepository(GameContext context) { this.context = context; this.dbSet = context.Set<TEntity>(); } public virtual IEnumerable<TEntity> Get( Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "") { IQueryable<TEntity> query = dbSet; if (filter != null) { query = query.Where(filter); } foreach (var includeProperty in includeProperties.Split (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { query = query.Include(includeProperty); } if (orderBy != null) { return orderBy(query).ToList(); } else { return query.ToList(); } } public virtual TEntity GetByID(object id) { return dbSet.Find(id); } public virtual void Insert(TEntity entity) { dbSet.Add(entity); } public virtual void Delete(object id) { TEntity entityToDelete = dbSet.Find(id); Delete(entityToDelete); } public virtual void Delete(TEntity entityToDelete) { if (context.Entry(entityToDelete).State == EntityState.Detached) { dbSet.Attach(entityToDelete); } dbSet.Remove(entityToDelete); } public virtual void Update(TEntity entityToUpdate) { dbSet.Attach(entityToUpdate); context.Entry(entityToUpdate).State = EntityState.Modified; } } }
DAL/GenericRepository.cs file
As we can see, first, we declare two class variables: one for the context and one for the entity set that the Repository is instantiated for. The constructor accepts a database context instance and initializes the entity set variable.
If we just overview the class, we can notice that the code declares a typical set of "CRUD" methods.
The signature of the "Get()" method can seem impressive. The first two arguments are lambda expressions. "Expression> filter" means that a lambda expression, based on "TEntity", will be provided and it will return a Boolean value. "Func, IOrderedQueryable> orderBy" also means that a lambda expression will be provided. The input of this expression is an "IQueryable" object and it will return an ordered version of that object. Finally, the third argument is a string that allows us to provide a comma-delimited list of navigation properties for eager loading. In the body of this method, we can see that, first, the filter expression is applied if there is one. Secondly, the eager-loading expression is performed. Finally, the order expression is applied if there is one.
Come next two methods, one to get an Entity by its ID, then a method that handles insertion. We then have two "Delete()" methods that manage the deletion. Finally, we find the "Update()" method.
Unit of Work
As we said, the role of the Unit of Work class is to make sure that, when we use multiple Repositories, they share a single database Context. So, when a Unit of Work is complete, the "SaveChanges()" method is called on the instance corresponding to the current Context. We are then assured that all related changes will be coordinated.
Let's create a file named "UnitOfWork.cs" in our "DAL" folder and fill it like so:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using GameApplication.Models; namespace GameApplication.DAL { public class UnitOfWork : IDisposable { private GameContext context = new GameContext(); private GenericRepository<Game> gameRepository; public GenericRepository<Game> GameRepository { get { return this.gameRepository ?? new GenericRepository<Game>(context); } } public void Save() { context.SaveChanges(); } private bool disposed = false; protected virtual void Dispose(bool disposing) { if (!this.disposed) { if (disposing) { context.Dispose(); } } this.disposed = true; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } } }
DAL/UnitOfWork.cs file
If we look at the code, we can see that the class implements the "IDisposable" interface. The main purpose of this interface is to release unmanaged resources. A managed resource means "managed memory" that is managed by the garbage collector. When we no longer have any references to a managed object, which uses managed memory, the garbage collector will release that memory for us. So, unmanaged resources are everything that the garbage collector does not know about (open files, open network connections, etc.). So, using the "Dispose()" method of this interface allows us to release unmanaged resources in conjunction with the garbage collector. The consumer of an object can call this method when the object is no longer needed.
We also have a class variable for the database Context and another for the Repository we are going to use. If there were more Repositories, we would have to add variables representing each of them. We then perform a check to see if our Repository already exists. If not, we instantiate it using the Context instance.
The "Save()" method calls the "SaveChanges()" on the Context.
Changing the Controller
Now, let's go back to our "GameController" and change it like so:
using System; using System.Collections.Generic; using System.Data; using System.Data.Entity; using System.Linq; using System.Net; using System.Web; using System.Web.Mvc; using GameApplication.DAL; using GameApplication.Models; namespace GameApplication.Controllers { public class GameController : Controller { private UnitOfWork unitOfWork = new UnitOfWork(); // GET: Game public ActionResult Index() { var games = unitOfWork.GameRepository.Get(); return View(games.ToList()); } // GET: Game/Details/5 public ActionResult Details(int? id) { if (id == null) { return new HttpStatusCodeResult(HttpStatusCode.BadRequest); } Game game = unitOfWork.GameRepository.GetByID(id); if (game == null) { return HttpNotFound(); } return View(game); } // GET: Game/Create public ActionResult Create() { return View(); } // POST: Game/Create [HttpPost] [ValidateAntiForgeryToken] public ActionResult Create([Bind(Include = "Id,Name")] Game game) { if (ModelState.IsValid) { unitOfWork.GameRepository.Insert(game); unitOfWork.Save(); return RedirectToAction("Index"); } return View(game); } // GET: Game/Edit/5 public ActionResult Edit(int? id) { if (id == null) { return new HttpStatusCodeResult(HttpStatusCode.BadRequest); } Game game = unitOfWork.GameRepository.GetByID(id); if (game == null) { return HttpNotFound(); } return View(game); } // POST: Game/Edit/5 [HttpPost] [ValidateAntiForgeryToken] public ActionResult Edit([Bind(Include = "Id,Name")] Game game) { if (ModelState.IsValid) { unitOfWork.GameRepository.Update(game); unitOfWork.Save(); return RedirectToAction("Index"); } return View(game); } // GET: Game/Delete/5 public ActionResult Delete(int? id) { if (id == null) { return new HttpStatusCodeResult(HttpStatusCode.BadRequest); } Game game = unitOfWork.GameRepository.GetByID(id); if (game == null) { return HttpNotFound(); } return View(game); } // POST: Game/Delete/5 [HttpPost, ActionName("Delete")] [ValidateAntiForgeryToken] public ActionResult DeleteConfirmed(int id) { Game game = unitOfWork.GameRepository.GetByID(id); unitOfWork.GameRepository.Delete(id); unitOfWork.Save(); return RedirectToAction("Index"); } protected override void Dispose(bool disposing) { if (disposing) { unitOfWork.Dispose(); } base.Dispose(disposing); } } }
Controllers/GameController.cs file edited
Here, we add a class variable for the "UnitOfWork". One improvement we could do here is to use the constructor instead or to use Dependency Injection. The main change is that every reference to the database Context is replaced by a reference to the appropriate Repository, using "UnitOfWork" properties to access the Repository.
If we run our application again, we see that it looks and works the same as before.
Conclusion
Through this article, we defined the Repository and the Unit of Work patterns. We saw which concept they encapsulate and how we can use them in our application.
One last word
If you like this article, you can consider supporting and helping me on Patreon! It would be awesome! Otherwise, you can find my other posts on Medium and Tumblr. You will also know more about myself on my personal website. Until next time, happy headache!
0 notes
iyarpage · 7 years
Text
Multiple Managed Object Contexts with Core Data Tutorial
This is an abridged from our book Core Data by Tutorials, which has been completely updated for Swift 4 and iOS 11. This tutorial is presented as part of our iOS 11 Launch Party — enjoy!
A managed object context is an in-memory scratchpad for working with your managed objects.
Most apps need just a single managed object context. The default configuration in most Core Data apps is a single managed object context associated with the main queue. Multiple managed object contexts make your apps harder to debug; it’s not something you’d use in every app, in every situation.
That being said, certain situations do warrant the use of more than one managed object context. For example, long-running tasks, such as exporting data, will block the main thread of apps that use only a single main-queue managed object context and cause the UI to stutter.
In other situations, such as when edits are being made to user data, it’s helpful to treat a managed object context as a set of changes that the app can discard if it no longer needs them. Using child contexts makes this possible.
In this tutorial, you’ll learn about multiple managed object contexts by taking a journaling app for surfers and improving it in several ways by adding multiple contexts.
Note: This is an advanced tutorial, and assumes prior knowledge of Swift, Core Data, and iOS app development in general. If common Core Data phrases such as managed object subclass and persistent store coordinator don’t ring any bells, or if you’re unsure what a Core Data stack is supposed to do, you may want to read some of our other Core Data tutorials first.
Getting Started
This tutorial’s starter project is a simple journal app for surfers. After each surf session, a surfer can use the app to create a new journal entry that records marine parameters, such as swell height or period, and rate the session from 1 to 5. Dude, if you’re not fond of hanging ten and getting barreled, no worries, brah. Just replace the surfing terminology with your favorite hobby of choice!
Introducing SurfJournal
Download the SurfJournal starter project here. Open the project, then build and run the app.
On startup, the application lists all previous surf session journal entries. Tapping a row brings up the detail view of a surf session with the ability to make edits.
As you can see, the sample app works and has data. Tapping the Export button on the top-left exports the data to a comma-separated values (CSV) file. Tapping the plus (+) button on the top-right adds a new journal entry. Tapping a row in the list opens the entry in edit mode, where you can change or view the details of a surf session.
Although the sample project appears simple, it actually does a lot and will serve as a good base to add multi-context support. First, let’s make sure you have a good understanding of the various classes in the project.
Open the project navigator and take a look at the full list of files in the starter project:
bela
Before jumping into the code, take a brief moment to go over what each class does for you out of the box.
AppDelegate: On first launch, the app delegate creates the Core Data stack and sets the coreDataStack property on the primary view controller JournalListViewController.
CoreDataStack: This object contains the cadre of Core Data objects known as the stack. Here, the stack installs a database that already has data in it on first launch. No need to worry about this just yet; you’ll see how it works shortly.
JournalListViewController: The sample project is a one-page, table-based application. This file represents that table. If you’re curious about its UI elements, head over to Main.storyboard. There’s a table view controller embedded in a navigation controller and a single prototype cell of type SurfEntryTableViewCell.
JournalEntryViewController: This class handles creating and editing surf journal entries. You can see its UI in Main.storyboard.
JournalEntry: This class represents a surf journal entry. It’s an NSManagedObject subclass with six properties for attributes: date, height, location, period, rating and wind. If you’re curious about this class’s entity definition, check out SurfJournalModel.xcdatamodel.
JournalEntry+Helper: This is an extension to the JournalEntry object. It includes the CSV export method csv() and the stringForDate() helper method. These methods are implemented in the extension to avoid being destroyed when you make changes to the Core Data model.
There was already a significant amount of data when you first launched the app.This sample project comes with a seeded Core Data database.
The Core Data Stack
Open CoreDataStack.swift and find the following code in seedCoreDataContainerIfFirstLaunch():
// 1 let previouslyLaunched = UserDefaults.standard.bool(forKey: "previouslyLaunched") if !previouslyLaunched { UserDefaults.standard.set(true, forKey: "previouslyLaunched") // Default directory where the CoreDataStack will store its files let directory = NSPersistentContainer.defaultDirectoryURL() let url = directory.appendingPathComponent( modelName + ".sqlite") // 2: Copying the SQLite file let seededDatabaseURL = Bundle.main.url( forResource: modelName, withExtension: "sqlite")! _ = try? FileManager.default.removeItem(at: url) do { try FileManager.default.copyItem(at: seededDatabaseURL, to: url) } catch let nserror as NSError { fatalError("Error: \(nserror.localizedDescription)") }
Here’s what this version of CoreDataStack.swift contains:
You first check UserDefaults for the previouslyLaunched boolean value. If the current execution is indeed the app’s first launch, the Bool will be false, making the if statement true. On first launch, the first thing you do is set previouslyLaunched to true so the seeding operation never happens again.
You then copy the SQLite seed file SurfJournalModel.sqlite, included with the app bundle, to the directory returned by the Core Data-provided method NSPersistentContainer.defaultDirectoryURL().
Now view the rest of seedCoreDataContainerIfFirstLaunch():
// 3: Copying the SHM file let seededSHMURL = Bundle.main.url(forResource: modelName, withExtension: "sqlite-shm")! let shmURL = directory.appendingPathComponent( modelName + ".sqlite-shm") _ = try? FileManager.default.removeItem(at: shmURL) do { try FileManager.default.copyItem(at: seededSHMURL, to: shmURL) } catch let nserror as NSError { fatalError("Error: \(nserror.localizedDescription)") } // 4: Copying the WAL file let seededWALURL = Bundle.main.url(forResource: modelName, withExtension: "sqlite-wal")! let walURL = directory.appendingPathComponent( modelName + ".sqlite-wal") _ = try? FileManager.default.removeItem(at: walURL) do { try FileManager.default.copyItem(at: seededWALURL, to: walURL) } catch let nserror as NSError { fatalError("Error: \(nserror.localizedDescription)") } print("Seeded Core Data") }
Once the copy of SurfJournalModel.sqlite has succeeded, you then copy over the support file SurfJournalModel.sqlite-shm.
Finally, you copy over the remaining support file SurfJournalModel.sqlite-wal.
The only reason SurfJournalModel.sqlite, SurfJournalModel.sqlite-shm or SurfJournalModel.sqlite-wal would fail to copy on first launch is if something really bad happened, such as disk corruption from cosmic radiation. In that case, the device, including any apps, would likely also fail. If the files fail to copy, there’s no point in continuing, so the catch blocks call fatalError.
Note: Developers often frown upon using abort and fatalError, as it confuses users by causing the app to quit suddenly and without explanation. This is one scenario where fatalError is acceptable, since the app needs Core Data to work. If an app requires Core Data and Core Data isn’t working, there’s no point in letting the app continue on, only to fail sometime later in a non-deterministic way.
Calling fatalError, at the very least, generates a stack trace, which can be helpful when trying to fix the problem. If your app has support for remote logging or crash reporting, you should log any relevant information that might be helpful for debugging before calling fatalError.
To support concurrent reads and writes, the persistent SQLite store in this sample app uses SHM (shared memory file) and WAL (write-ahead logging) files. You don’t need to know how these extra files work, but you do need to be aware of their existence, and that you need to copy them over when seeding the database. If you fail to copy over these files, the app will work, but it might be missing data.
Now that you know something about beginning with a seeded database, you’ll start learning about multiple managed object contexts by working on a temporary private context.
Doing Work In the Background
If you haven’t done so already, tap the Export button at the top-left and then immediately try to scroll the list of surf session journal entries. Notice anything? The export operation takes several seconds, and it prevents the UI from responding to touch events such as scrolling.
The UI is blocked during the export operation because both the export operation and UI are using the main queue to perform their work. This is the default behavior.
The traditional way to fix this is to use Grand Central Dispatch to run the export operation on a background queue. However, Core Data managed object contexts are not thread-safe. That means you can’t just dispatch to a background queue and use the same Core Data stack.
The solution is simple: use a private background queue rather than the main queue for the export operation. This will keep the main queue free for the UI to use.
But before you jump in and fix the problem, you need to understand how the export operation works.
Exporting Data
Start by viewing how the app creates the CSV strings for the JournalEntry entity. Open JournalEntry+Helper.swift and find csv():
func csv() -> String { let coalescedHeight = height ?? "" let coalescedPeriod = period ?? "" let coalescedWind = wind ?? "" let coalescedLocation = location ?? "" let coalescedRating: String if let rating = rating?.int32Value { coalescedRating = String(rating) } else { coalescedRating = "" } return "\(stringForDate()),\(coalescedHeight),\(coalescedPeriod),\(coalescedWind),\(coalescedLocation),\(coalescedRating)\n" }
As you can see, JournalEntry returns a comma-separated string of the entity’s attributes. Because the JournalEntry attributes are allowed to be nil, the function uses the nil coalescing operator (??) to export an empty string instead of an unhelpful debug message that the attribute is nil.
Note: The nil coalescing operator (??) unwraps an optional if it contains a value; otherwise it returns a default value. For example, the following: let coalescedHeight = height != nil ? height! : "" can be shortened using the nil coalescing operator to: let coalescedHeight = height ?? "".
That’s how the app creates the CSV strings for an individual journal entry, but how does the app save the CSV file to disk? Open JournalListViewController.swift and find the following code in exportCSVFile():
// 1 let context = coreDataStack.mainContext var results: [JournalEntry] = [] do { results = try context.fetch(self.surfJournalFetchRequest()) } catch let error as NSError { print("ERROR: \(error.localizedDescription)") } // 2 let exportFilePath = NSTemporaryDirectory() + "export.csv" let exportFileURL = URL(fileURLWithPath: exportFilePath) FileManager.default.createFile(atPath: exportFilePath, contents: Data(), attributes: nil)
Going through the CSV export code step-by-step:
First, retrieve all JournalEntry entities by executing a fetch request.
The fetch request is the same one used by the fetched results controller. Therefore, you reuse the surfJournalFetchRequest method to create the request to avoid duplication.
Next, create the URL for the exported CSV file by appending the file name (“export.csv”) to the output of the NSTemporaryDirectory method.
The path returned by NSTemporaryDirectory is a unique directory for temporary file storage. This a good place for files that can easily be generated again and don’t need to be backed up by iTunes or to iCloud.
After creating the export URL, call createFile(atPath:contents:attributes:) to create the empty file where you’ll store the exported data. If a file already exists at the specified file path, this method will remove it first.
Once the app has the empty file, it can write the CSV data to disk:
// 3 let fileHandle: FileHandle? do { fileHandle = try FileHandle(forWritingTo: exportFileURL) } catch let error as NSError { print("ERROR: \(error.localizedDescription)") fileHandle = nil } if let fileHandle = fileHandle { // 4 for journalEntry in results { fileHandle.seekToEndOfFile() guard let csvData = journalEntry .csv() .data(using: .utf8, allowLossyConversion: false) else { continue } fileHandle.write(csvData) } // 5 fileHandle.closeFile() print("Export Path: \(exportFilePath)") self.navigationItem.leftBarButtonItem = self.exportBarButtonItem() self.showExportFinishedAlertView(exportFilePath) } else { self.navigationItem.leftBarButtonItem = self.exportBarButtonItem() }
Here’s how the file-handling works:
First, the app needs to create a file handler for writing, which is simply an object that handles the low-level disk operations necessary for writing data. To create a file handler for writing, use the FileHandle(forWritingTo:) initializer.
Next, iterate over all JournalEntry entities.
During each iteration, you attempt to create a UTF8-encoded string using csv() on JournalEntry and data(using:allowLossyConversion:) on String.
If it’s successful, you write the UTF8 string to disk using the file handler write() method.
Finally, close the export file-writing file handler, since it’s no longer needed.
Once the app has written all the data to disk, it shows an alert dialog with the exported file path.
Note: This alert controller with the export path is fine for learning purposes, but for a real app, you’ll need to provide the user with a way to retrieve the exported CSV file, for example using UIActivityViewController.
To open the exported CSV file, use Excel, Numbers or your favorite text editor to navigate to and open the file specified in the alert dialog. If you open the file in Numbers you will see the following:
Now that you’ve seen how the app currently exports data, it’s time to make some improvements.
Exporting In the Background
You want the UI to continue working while the export is happening. To fix the UI problem, you’ll perform the export operation on a private background context instead of on the main context.
Open JournalListViewController.swift and find the following code in exportCSVFile():
// 1 let context = coreDataStack.mainContext var results: [JournalEntry] = [] do { results = try context.fetch(self.surfJournalFetchRequest()) } catch let error as NSError { print("ERROR: \(error.localizedDescription)") }
As you saw earlier, this code retrieves all of the journal entries by calling fetch() on the managed object context.
Next, replace the above code with the following:
// 1 coreDataStack.storeContainer.performBackgroundTask { context in var results: [JournalEntry] = [] do { results = try context.fetch(self.surfJournalFetchRequest()) } catch let error as NSError { print("ERROR: \(error.localizedDescription)") }
Instead of using the main managed object context also used by the UI, you’re now calling performBackgroundTask(_:) method. This creates and executes the code block on that private context.
The private context created by performBackgroundTask(_:) is on a private queue, which doesn’t block the main UI queue. You could also manually create a new temporary private context with a concurrency type of .privateQueueConcurrencyType instead of using performBackgroundTask(_:).
Note: There are two concurrency types a managed object context can use:
Private Queue specifies the context that will be associated with a private dispatch queue instead of the main queue. This is the type of queue you just used to move the export operation off of the main queue so it would no longer interfere with the UI.
Main Queue, the default type, specifies that the context will be associated with the main queue. This type is what the main context (coreDataStack.mainContext) uses. Any UI operation, such as creating the fetched results controller for the table view, must use a context of this type.
Next, find the following code in the same method:
print("Export Path: \(exportFilePath)") self.navigationItem.leftBarButtonItem = self.exportBarButtonItem() self.showExportFinishedAlertView(exportFilePath) } else { self.navigationItem.leftBarButtonItem = self.exportBarButtonItem() }
Replace the code with the following:
print("Export Path: \(exportFilePath)") // 6 DispatchQueue.main.async { self.navigationItem.leftBarButtonItem = self.exportBarButtonItem() self.showExportFinishedAlertView(exportFilePath) } } else { DispatchQueue.main.async { self.navigationItem.leftBarButtonItem = self.exportBarButtonItem() } } } // 7 Closing brace for performBackgroundTask
To finish off the task:
You should always perform all operations related to the UI on the main queue, such as showing an alert view when the export operation is finished; otherwise, unpredictable things might happen. Use DispatchQueue.main.async to show the final alert view message on the main queue.
Finally, add a closing curly brace to close the block you opened earlier in step 1 via the performBackgroundTask(_:) call.
surf
Now that you’ve moved the export operation to a new context with a private queue, build and run to see if it works!
You should see exactly what you saw before:
Tap the Export button in the top left, and immediately try to scroll the list of surf session journal entries. Notice anything different this time? The export operation still takes several seconds to complete, but the table view continues to scroll during this time. The export operation is no longer blocking the UI.
Cowabunga, dude! Gnarly job making the UI more responsive.
You’ve just witnessed how doing work on a private background queue can improve a user’s experience with your app. Now you’ll expand on the use of multiple contexts by examining a child context.
Editing On a Scratchpad
Right now, SurfJournal uses the main context (coreDataStack.mainContext) when creating a new journal entry or viewing an existing one. There’s nothing wrong with this approach; the starter project works as-is.
For journaling-style apps like this one, you can simplify the app architecture by thinking of edits or new entries as a set of changes, like a scratch pad. As the user edits the journal entry, you update the attributes of the managed object. Once the changes are complete, you either save them or throw them away, depending on what the user wants to do.
You can think of child managed object contexts as temporary scratch pads that you can either discard completely, or save and send the changes to the parent context.
But what is a child context, technically?
All managed object contexts have a parent store from which you can retrieve and change data in the form of managed objects, such as the JournalEntry objects in this project. Typically, the parent store is a persistent store coordinator, which is the case for the main context provided by the CoreDataStack class. Alternatively, you can set the parent store for a given context to another managed object context, making it a child context.
When you save a child context, the changes only go to the parent context. Changes to the parent context won’t be sent to the persistent store coordinator until the parent context is saved.
Before you jump in and add a child context, you need to understand how the current viewing and editing operation works.
Viewing and Editing
The first part of the operation requires segueing from the main list view to the journal detail view. Open JournalListViewController.swift and find prepare(for:sender:):
// 1 if segue.identifier == "SegueListToDetail" { // 2 guard let navigationController = segue.destination as? UINavigationController, let detailViewController = navigationController.topViewController as? JournalEntryViewController, let indexPath = tableView.indexPathForSelectedRow else { fatalError("Application storyboard mis-configuration") } // 3 let surfJournalEntry = fetchedResultsController.object(at: indexPath) // 4 detailViewController.journalEntry = surfJournalEntry detailViewController.context = surfJournalEntry.managedObjectContext detailViewController.delegate = self
Taking the segue code step-by-step:
There’s two segues: SegueListToDetail and SegueListToDetailAdd. The first, shown in the previous code block, runs when the user taps on a row in the table view to view or edit a previous journal entry.
Next, you get a reference to the JournalEntryViewController the user is going to end up seeing. It’s presented inside a navigation controller so there’s some unpacking to do. This code also verifies that there’s a selected index path in the table view.
Next, you get the JournalEntry selected by the user, using the fetched results controller’s object(at:) method.
Finally, you set all required variables on the JournalEntryViewController instance. The surfJournalEntry variable corresponds to the JournalEntry entity resolved in step 3. The context variable is the managed object context to be used for any operation; for now, it just uses the main context. The JournalListViewController sets itself as the delegate of the JournalEntryViewController so it can be informed when the user has completed the edit operation.
SegueListToDetailAdd is similar to SegueListToDetail, except the app creates a new JournalEntry entity instead of retrieving an existing one. The app executes SegueListToDetailAdd when the user taps the plus (+) button on the top-right to create a new journal entry.
Now that you know how both segues work, switch to JournalEntryViewController.swift and look at the JournalEntryDelegate protocol at the top of the file:
protocol JournalEntryDelegate { func didFinish(viewController: JournalEntryViewController, didSave: Bool) }
The JournalEntryDelegate protocol is very short and consists of only one method: didFinish(viewController:didSave:). This method, which the protocol requires the delegate to implement, indicates if the user is done editing or viewing a journal entry and whether any changes should be saved.
To understand how didFinish(viewController:didSave:) works, switch back to JournalListViewController.swift and find that method:
func didFinish(viewController: JournalEntryViewController, didSave: Bool) { // 1 guard didSave, let context = viewController.context, context.hasChanges else { dismiss(animated: true) return } // 2 context.perform { do { try context.save() } catch let error as NSError { fatalError("Error: \(error.localizedDescription)") } // 3 self.coreDataStack.saveContext() } // 4 dismiss(animated: true) }
Taking each numbered comment in turn:
First, use a guard statement to check the didSave parameter. This will be true if the user taps the Save button instead of the Cancel button, so the app should save the user’s data. The guard statement also uses the hasChanges property to check if anything’s changed; if nothing has changed, there’s no need to waste time doing more work.
Next, save the JournalEntryViewController context inside of a perform(_:) closure. The code sets this context to the main context; in this case it’s a bit redundant since there’s only one context, but this doesn’t change the behavior.
Once you add a child context to the workflow later on, the JournalEntryViewController context will be different from the main context, making this code necessary.
If the save fails, call fatalError to abort the app with the relevant error information.
Next, save the main context via saveContext, defined in CoreDataStack.swift, persisting any edits to disk.
Finally, dismiss the JournalEntryViewController.
Note: If a managed object context is of type MainQueueConcurrencyType, you don’t have to wrap code in perform(_:), but it doesn’t hurt to use it.
If you don’t know what type the context will be, as is the case in didFinish(viewController:didSave:), it’s safest to use perform(_:) so it will work with both parent and child contexts.
There’s a problem with the above implementation — have you spotted it?
When the app adds a new journal entry, it creates a new object and adds it to the managed object context. If the user taps the Cancel button, the app won’t save the context, but the new object will still be present. If the user then adds and saves another entry, the canceled object will still be present! You won’t see it in the UI unless you’ve got the patience to scroll all the way to the end, but it will show up at the bottom of the CSV export.
You could solve this problem by deleting the object when the user cancels the view controller. But what if the changes were complex, involved multiple objects, or required you to alter properties of an object as part of the editing workflow? Using a child context will help you manage these complex situations with ease.
Using Child Contexts for Sets of Edits
Now that you know how the app currently edits and creates JournalEntry entities, you’ll modify the implementation to use a child managed object context as a temporary scratch pad.
It’s easy to do — you simply need to modify the segues. Open JournalListViewController.swift and find the following code for SegueListToDetail in prepare(for:sender:):
detailViewController.journalEntry = surfJournalEntry detailViewController.context = surfJournalEntry.managedObjectContext detailViewController.delegate = self
Next, replace that code with the following:
// 1 let childContext = NSManagedObjectContext( concurrencyType: .mainQueueConcurrencyType) childContext.parent = coreDataStack.mainContext // 2 let childEntry = childContext.object( with: surfJournalEntry.objectID) as? JournalEntry // 3 detailViewController.journalEntry = childEntry detailViewController.context = childContext detailViewController.delegate = self
Here’s the play-by-play:
First, you create a new managed object context named childContext with a .mainQueueConcurrencyType. Here you set a parent context instead of a persistent store coordinator as you would normally do when creating a managed object context. Here, you set parent to mainContext of your CoreDataStack.
Next, you retrieve the relevant journal entry using the child context’s object(with:) method. You must use object(with:) to retrieve the journal entry because managed objects are specific to the context that created them. However, objectID values are not specific to a single context, so you can use them when you need to access objects in multiple contexts.
Finally, you set all required variables on the JournalEntryViewController instance. This time, you use childEntry and childContext instead of the original surfJournalEntry and surfJournalEntry.managedObjectContext.
Note: You might be wondering why you need to pass both the managed object and the managed object context to the detailViewController, since managed objects already have a context variable. This is because managed objects only have a weak reference to the context. If you don’t pass the context, ARC will remove the context from memory (since nothing else is retaining it) and the app will not behave as you expect.
Build and run your app; it should work exactly as before. In this case, no visible changes to the app are a good thing; the user can still tap on a row to view and edit a surf session journal entry.
By using a child context as a container for the journal edits, you’ve reduced the complexity of your app’s architecture. With the edits on a separate context, canceling or saving managed object changes is trivial.
Nice work, dude! You’re no longer a kook when it comes to multiple managed object contexts. Bodacious!
Where to Go From Here?
You can download the finished project from this tutorial here.
If you followed this tutorial all the way through, you’ve turned an app with a single managed object context into an app with multiple contexts.
You improved UI responsiveness by performing the export operation on a private background managed object context, and you improved the app’s architecture by creating a child context and using it like a scratch pad.
But best of all, you learned how to talk like a surfer. That’s a good day’s work!
If you enjoyed what you learned in this tutorial, why not check out the complete Core Data by Tutorials book, available in our store?
Here’s a taste of what’s in the book:
1. Chapter 1, Your First Core Data App: You’ll click File\New Project and write a Core Data app from scratch! This chapter covers the basics of setting up your data model and then adding and fetching records.
2. Chapter 2, NSManagedObject Subclasses: NSManagedObject is the base data storage class of your Core Data object graphs. This chapter will teach you how you customize your own managed object subclasses to store and validate data.
3. Chapter 3, The Core Data Stack: Under the hood, Core Data is made up of many parts working together. In this chapter, you’ll learn about how these parts fit together, and move away from the starter Xcode template to build your own customizable system.
4. Chapter 4, Intermediate Fetching: Your apps will fetch data all the time, and Core Data offers many options for getting the data to you efficiently. This chapter covers more advanced fetch requests, predicates, sorting and asynchronous fetching.
5. Chapter 5, NSFetchedResultsController: Table views are at the core of many iOS apps, and Apple wants to make Core Data play nicely with them! In this chapter, you’ll learn how NSFetchedResultsController can save you time and code when your table views are backed by data from Core Data.
6. Chapter 6, Versioning and Migration: As you update and enhance your app, its data model will almost certainly need to change. In this chapter, you’ll learn how to create multiple versions of your data model and then migrate your users forward so they can keep their existing data as they upgrade.
7. Chapter 7, Unit Tests: Testing is an important part of the development process, and you shouldn’t leave Core Data out of that! In this chapter, you’ll learn how to set up a separate test environment for Core Data and see examples of how to test your models.
8. Chapter 8, Measuring and Boosting Performance: No one ever complained that an app was too fast, so it’s important to be vigilant about tracking performance. In this chapter, you’ll learn how to measure your app’s performance with various Xcode tools and then pick up some tips for dealing with slow spots in your code.
9. Chapter 9, Multiple Managed Object Contexts: In this final chapter, you’ll expand the usual Core Data stack to include multiple managed object contexts. You’ll learn how this can improve perceived performance and help make your app architecture less monolithic and more compartmentalized.
And to help sweeten the deal, the digital edition of the book is on sale for $49.99! But don’t wait — this sale price is only available for a limited time.
Speaking of sweet deals, be sure to check out the great prizes we’re giving away this year with the iOS 11 Launch Party, including over $9,000 in giveaways!
To enter, simply retweet this post using the #ios11launchparty hashtag by using the button below:
Tweet !function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');
We hope you enjoy this update, and stay tuned for more book releases and updates!
The post Multiple Managed Object Contexts with Core Data Tutorial appeared first on Ray Wenderlich.
Multiple Managed Object Contexts with Core Data Tutorial published first on http://ift.tt/2fA8nUr
0 notes
baburaja97-blog · 8 years
Text
New Post has been published on Vin Zite
New Post has been published on https://vinzite.com/slight-exertion-with-objective-c-for-iphone-development/
Slight Exertion With Objective-C For iPhone Development
Introduction
Objective-C is a programming language designed to enable sophisticated object-oriented programming; it is nothing more than a superset of the C programming language and it supports all the basic syntax of the C language, which means that theoretically a program written in C should compile as an Objective-C program without any change.
Most object-oriented environments which is provided for the development consist of several parts, some of them are:
A language which is object-oriented programming
A library of objects
A package of development tools
A runtime environment
What will you need?
I will not focus on building iPhone applications throughout this tutorial because first you learn the programming language used in the iPhone app development. This tutorial will concentrate more on the language (Objective-C) basic concepts and for that you just need a Mac with a compiler like GNU Compiler Collection (GCC).
If you have already installed the Xcode package and Interface Builder from Apple’s Site then GCC was automatically installed. If not, then first you will need to register for an Apple Developer Connection account. Once you have registered, login and click Download Software then in Developer Tools you can download the required software.
Windows users (which is unlikely because this tutorial is written aimed at iPhone developers) can also compile Objective-C on your system using CygWin or the MinGW compiler.
Here you will learn the skills necessary to program in Objective-C using a style that is easy to follow, rich in examples and accessible to those who have never used Objective-C before.
Here we covered the fundamentals of Objective-C and object oriented programming. So, for that let’s quickly look at the predefined Data Types and Operators of Objective-C.
List of data types and qualifiers supported by Objective-C
int Data Type
char Data Type
float Data Type
double Data Type
id Data Type
BOOL Data Type
Special Characters/Escape Sequences
a – Sound alert
b – Backspace
f – Form feed
n – New line
r – Carriage return
t – Horizontal tab
v – Vertical tab
– Backslash
” – Double quote (It is using when we want to place a double quote into a string declaration)
‘ – Single quote (It is using when we want to place a double quote into a string declaration)
Objective-C Data Type Qualifiers
long
long long
short
signed / unsigned
Now you we will look the list of Objective-C Operators
Arithmetic Operators
Operator Description
-(unary) Negates the value
* Multiplication
/ Division
+ Addition
– Subtraction
% Modulo
Assignment Operators
Compound Assignment Operators
Operator Description
x += y It is used to Add x and y, and store result in x.
x -= y It is used to Subtract y from x and store result in x.
x *= y It is used to Multiply x by y and store result in x.
x /= y It is used to Divide x by y and store result in x.
x %= y It is used to Modulo on x and y and store result in x.
x &= y Send the result to x of logical AND operation on x and y.
x |= y Send the result to x of logical OR operation on x and y.
x ^= y Send the result to x of logical Exclusive OR on x and y.
Increment and Decrement Operators
Comparison Operators
Operator Description
x == y True if x is equal to y.
x > y True if x is greater than y.
x >= y True if x is greater than or equal to y.
x >= n Shift x right by n places and send result to x.
Objective-C requires the interface, implementation and instantiation of a class to be in separately declared code blocks which use the file extensions (I will describe that in detail later in this article): Extension
Source type .h It holds the class, type, function, and constant declarations. .m It holds the source files and can contain both Objective-C and C code. .mm It contain C++ code in addition to Objective-C and C code. This extension should be used only if you actually refer to C++ classes or features from your Objective-C code.
First Program: Welcome Objective C
Starting with a simple “Welcome Objective C” program as I am written this tutorial for iPhone Development, I choose Xcode for creating Objective-C programs. Start writing the program by launching Xcode; you just need to choose the “Foundation Tool” under “Command Line Utility” and give a name to your program. For example I chose here “WelcomeObjectiveC”, now the project window will open and you will see the highlighted file, “WelcomeObjectiveC.m”, start writing your program in this file.https://vinzite.com/wp-content/uploads/2017/03/AAEAAQAAAAAAAAesAAAAJGRhNWM0NGFhLTEzZjktNDk1ZC1iZGNhLWZlMzcyMGY4YTJhMw-3.jpg
Example #import int main (int argc, const char *argv[])
NSLog (@”Welcome, Objective C”);
return (0);
Now first build the program and then run it by clicking the Build and Go button or pressing. If there aren’t any nasty errors in your program, Xcode successfully compiles and runs your program.
To see the output of your program, open the Xcode console window. You can open the console window by selecting Console from the Run menu or pressing.
And there you have the output in the console window: Welcome, Objective C
Now see how the code works.
In the first line I import the Foundation.h file and this whole import statement informs the compiler to look at the Foundation.h header file in the Foundation framework.
Now you ask what is Foundation Framework? OK, first see what is Framework: A framework is a collection of parts-header files, libraries, images, sounds, and more-collected together into a single unit.
Foundation Framework: The Foundation framework defines a base layer of Objective-C classes and the Foundation framework is designed with these goals in mind (said by Apple):
It is provides a small set of basic utility classes used in the programs.
Introduces consistent conventions such as Deallocation which make software development easier.
Supports Unicode strings, object persistence, and object distribution.
Provide a level of OS independence, to enhance portability.
Next is Main Function int main (int argc, const char *argv[])
//Function Code
The main function (as in standard C) tells the Objective-C compiler where to start program execution. In the main function you see the various fields as follows:
return type: Specifies the data type of the result returned by the function. If the function does not return a result then void should be specified.
function name: It is the name by which the function will be referenced when it is called from within the application code. Note that, unless otherwise specified using the static specifier, function names are global and must be unique within the context of an application to avoid compilation errors.
argn type: The type of the argument passed through to the function.
argn name: The name by which the argument is to be referenced in the function code.
Now comes to NSLog() and @”strings” NSLog (@”Welcome, Objective C”);
Here NSLog() is a Cocoa function which is used to print the value in Objective-C. In NSLog we pass the string between double quotes started with @ symbol that is represent as Objective-C language keywords.
After execution of main() is finished the zero value is returned by a “return (0);” statement which says that our program completed successfully.
Interface, Implementation and Instantiation
You can use Objective C to develop full fledged iPhone applications; it revolves around objects. It has three parts:
Interface
Implementation
Instantiation
Interface
Interface is a object class in Objective-C, by convention, developers place the interface in a header file, it contains the class declaration and defines the instance variables and methods associated with the class. The interface is suffixed by.h, parentheses contained all the variables which is associated with the interface, but the methods are declared outside of the parentheses, yet still inside the structure formed by an opening @interface and a closing @end.
Structure of Interface @interface Fraction: NSObject
// instance variables
int numerator;
int denominator; -(void) setNumerator: (int) num; -(void) setDenominator: (int) den; -(void) print; @end //Fraction
Code Walk
Start from the first line: @interface Fraction: NSObject
The above line of code informs the compiler that a new interface for a new class named Fraction is created and NSObject specifies that the Fraction class is based/inherited from the NSObject class.
// instance variables
int numerator;
int denominator;
Now to the template design; the above is a template we create within curly braces which is used to work with the Fraction object. Here we declare two elements numerator type-of integer and denominator again type-of integer, we can say they are instance variables to these elements.
When we talk about the variable declaration, first we need to tell the compiler what we’re going to store and in what type-of. Here are some of the most important variables types that you should know about for now:
int: For storing integers (numbers with no decimal point).
char: For storing a character.
float: For storing numbers with decimal points.
double: Same as a float but double the accuracy.
Now come to method declaration. -(void) setNumerator: (int) num; -(void) setDenominator: (int) den; -(void) print;
The above code lines are just method declarations. In a method declaration you have to give the name of each method, the method’s return type, and any arguments. But the above code looks a little different; not a simple method declaration, isn’t it? Yep, first remember that methods are of two types and the defining syntax is also two types:
First which are defined using the plus sign (+)
Second which are defined using the negative sign (-)
What is difference between these two methods? Methods which define using the plus sign (+) are class methods, they operate on all instances of the interface. They do not have access to any specific instance variables defined in the interface. Methods defined using the negative sign (-) are instance methods, they operate on a specific instance of the interface. They can access the variables associated with an instance of the interface.
Returning to the previous code, in the first two lines we just start the method declaration with a leading dash sign and the return type is in parentheses then the name of the method “setNumerator and setDenominator” then the trailing colon after the method name is a hint to compilers and humans that a parameter is coming next, then the type of the argument is in parentheses and at last the parameter name exists.
In the last line of the method declarations part we just print without returning anything. @end // Fraction
We write the above line at the end; it ends the declaration of the Fraction class.
Implementation
After the interface declaration let’s proceed to the implementation of the interface; as you have learned the interface only declares the class interface and not the methods themselves; the actual code is written in the implementation. Implementation (method) files normally have the file extension.m. Interface implementation is like definition of the interface. All implementations are contained within an @implementation… @end block. One more thing that must be done when the implementation is done, that is importing the interface.
Structure of implementation #import @implementation Fraction} -(void) print
printf( “%i/%i”, numerator, denominator ); -(void) setNumerator: (int) numN
numerator = numN; -(void) setDenominator: (int) denD
denominator = denD; @end
Code Walk
Start from the first line: #import
As I already explained the Objective-C interface definition was contained in the file “Fraction.h”. At the time of implementation we need to import this file at the beginning such as shown in the above code. Why? To include any module that creates an instance of the class, sends a message to invoke a method declared for the class, or mentions an instance variable declared in the class.
Implementation for class Fraction @implementation Fraction
In the above code @implementation is a directive which introduces the code for the guts of a class. After witting @implementation we write the name of the class. There is no trailing semicolon on this line, because you don’t need semicolons after Objective-C directives.
Now define the individual methods -(void) print
printf( “%i/%i”, numerator, denominator ); -(void) setNumerator: (int) numN
numerator = numN; -(void) setDenominator: (int) denD
denominator = denD;
In the first line we print the values and you will see a sign of “%i” that is a formatting sign used because we were including an integer. Other variables have their own format specifiers:
%i: integer
%f: float
%e: double
%c: char
That actually implements the methods; in the above code we declared methods in the same manner as in the interface file, but without the semicolon. One thing more to notice here is that we renamed the parameters because it’s better for the parameter names to differ between the @interface and the @implementation. Then inside the body of the method code will refers to the instance variables.
The last line: @end
Closes the block of implementation.
Instantiation
When you are using Objective-C on the iPhone you will need to allocate memory as well as create an object. To allocate memory you send an alloc message to the class itself; this process is called instantiating. When you instantiate an object, memory is allocated, and then that memory is initialized to some useful default values; that is, something other than the random values you get with freshly allocated memory.
Structure of Instantiation int main( int argc, const char *argv[] )
// creating a new instance
Fraction *frac = [[Fraction alloc] init];
// set values
[frac setNumerator: 2];
[frac setDenominator: 5];
// simple print
printf( “The value of fraction is: ” );
[frac print]; printf( “n” );
// release the memory
[frac release];
return 0;
Here the output will be: The value of fraction is: 2/5
Code Walk
Instance Creation Fraction *frac = [[Fraction alloc] init];
The above is how you instantiate an object, here we will use a constructor that is generally prefixed with the word “init” or simply init. An initializer returns an instance – usually the same instance, initialized. Therefore you can, and always should, call alloc and the initializer in the same line of code.
Set the values [frac setNumerator: 2]; [frac setDenominator: 5];
The above code line is just simply calling the setNumerator and setDenominator method on frac, and passing it the parameter 2 and 5.
Printing printf( “The value of fraction is: ” ); [frac print]; printf( “n” );
This is simply calling the print method on frac and prints the values/output.
Free the memory [frac release];
When using Objective-C on the iPhone you must release objects that you have created using alloc, new or copy. This has to do with memory management. This is also functionally equivalent to C/C++; all three languaes do not use garbage collection. To release an object you simply send the “release” message to the object.
Let’s Program with Multiple Parameters
In Objective-C you can use a method with multiple parameters. These parameters can be of the same type or of a different type.
Let’s look at an example to clarify use of multiple parameters:
In class.h #import @interface Class:NSObject
// declare method for multiple parameter
-(int) sub: (int) i andj: (int) j andk:(int)k; @end
The main thing is that the separator in the arguments are “:”; you can set any number of arguments using “:”.
In class.m #import #import”Class.h” @implementation MyClass -(int) sub: (int) i andj: (int) j andk:(int)k;
return i+j+k; @end
In main #import #import”Class.m” int main()
Class *class = [[Class alloc]init];
printf(“Subtraction is: %d”,[class sub: 16 andj: 8 andk:5]);
[class release]; return;
The Output is: Subtraction is: 3
Designated Constructor
Constructors are to create and initialize an instance of a class. For a class, there can be multiple constructors with various parameters expecting some initial variables. But in Objective-C, first you allocate memory on the heap. Next you call a method called an initializer, which is kind of Objective-C’s equivalent of a constructor.
See the Example
Class Header: Employee.h #import @interface Employee: NSObject
NSString* name;
int age;
int mobileNumber;
Class Implementation: Employee.m #import “Employee.h” @implementation Employee -(id)init
self = [super init];
if(self)
//Initialization code here.
name=@””;
age=0;
mobilenumber=0;
return self; @end
Code Walk
First I created a standard Employee class; it does not consist of any methods, just some simple member variables; namely name, age, and mobilenumber.
Then in the implementation you see a method called “init”. It is the standard initializer method that Xcode creates as a boilerplate for each new class file you create, this method is used to initialize a newly created object and put it into a safe state, exactly as constructors do in C# or C++.
Access Privileges
In Objective-C, access privilege can only be specified on variables. Default access in objective-C is @protected. Types of access privileges are:
@public
@private
@protected
The @protected accessor enables access elements in the subclass.
Exception handling
Using GNU Compiler Collection (GCC) version 3.3 and later, Objective-C provides language-level support for exception handling. An exception is a special condition that interrupts the normal flow of program execution. There are a variety of reasons why an exception may be generated. It works like in every other modern language: they are represented by classes inheriting (usually) from NSException.
NOTE: Exception handling is only supported in Mac OS X 10.3
Compiler directives used for exception handling:
@try: block which throws an exception.
@catch: defines how to handle exceptions thrown by a try block; this is usually an NSException object.
@finally: it is executed whether an exception is thrown or not.
@throw: once your program detects an exception, it must propagate the exception to code that handles it. This code is called the exception handler. This entire
process of propagating an exception is referred to as “throwing an exception”.
Exception-Handling Algorithm mug *mug = [[mug alloc] init]; @try
[mug fill]; @catch (NSException *exception)
NSLog(@”main: Caught %@: %@”, [exception name], [exception reason]); @finally
[mug release];
Catching the Exception @try … @catch (CustomException *cex) … @catch (NSException *nex)
// Processing necessary at this level… @catch (id uex) … @finally
// Processing necessary…
Throwing Exceptions NSException *exception = [NSException exceptionWithName: @”MugfullException”reason: @”The mug is full of water”userInfo: nil]; @throw exception;
Inheritance
Inheritance brings something of a real-world view to programming. It allows a class to be defined that has a certain set of characteristics and then other classes can be created derived from that class. The derived class inherits all of the features of the parent class and typically then adds some features of its own. In addition to inheriting from classes we can also extend classes. When we extend a class, we take an existing class and add additional functionality to what is already available.
inheritance.gif
Let?s understand the above diagram, first take NSMutableString. NSMutableString has NSString for a parent, meaning it inherits from NSString. Meanwhile, NSString has NSObject as a parent, so it inherits from NSObject, then NSMutableArray has NSArray for a parent, which means it inherits from NSArray. Meanwhile, NSArray has NSObject as a parent, so it inherits from NSObject, then the last NSNumber has NSValue for a parent, meaning it inherits from NSValue. Meanwhile, NSValue also NSObject as a parent, so it inherits from NSObject.
Is new technology nothing without new terms to learn? Exactly, so here are some new terms of inheritance:
Super class: The superclass is the class you were inheriting from.
Parent class: Another word for “superclass” is Parent class.
Sub class: The subclass is the class doing the inheriting.
Child class: Child class is another word for ”subclass”.
Override: You override an inherited method when you want to change its implementation.
Let’s take a Circle and Rectangle example of see how inheritance works. Look at the following figure, I am going to explain this by example:
inheritance1.gif
In drawing.h @interface drawing: NSObject
DrawShape bounds; – (void) setBounds: (DrawShape) bounds; – (void) draw; @end // drawing
In drawing.m @implementation drawing – (void) setBounds: (DrawShape) b
bounds = b; // setBounds – (void) draw // draw @end // drawing
In Circle.h @interface Circle: NSObject
DrawShapebounds; – (void) setBounds: (DrawShape) bounds; – (void) draw; @end // Circle
In Circle.m @implementation Circle – (void) draw
NSLog (@”drawing a circle at (%d %d %d %d) in %@”, bounds.x,
bounds.y, bounds.width, bounds.height, // draw @end // Circle
In Rectangle.h @interface Rectangle: NSObject
DrawShape bounds; – (void) setBounds: (DrawShape) bounds; – (void) draw; @end // Rectangle
In Rectangle.m @implementation Rectangle – (void) draw
NSLog (@”drawing rect at (%d %d %d %d) in %@”, bounds.x, bounds.y,
bounds.width, bounds.height, // draw @end // Rectangle
In main int main (int argc, const char * argv[])
id shapes[2];
DrawShape shap1 = 5, 5, 20, 40 ;
shapes[0] = [Circle new];
[shapes[0] setBounds: rect0];
DrawShape shap2 = 50, 60, 70, 80 ;
shapes[1] = [Rectangle new];
[shapes[1] setBounds: rect1];
return (0); // main
In the output you will see:
Drawing a circle at (5 5 20 40) Drawing a rectangle at (50 50 60 70)
Memory Management
In the Apple’s library it says “The basic model used for memory management in a reference-counted environment is provided by a combination of methods defined in the NSObject protocol and a standard method naming convention. The NSObject class also defines a method, dealloc, that is invoked automatically when an object is deallocated.”
memory_management.jpg
For memory management you call [obj retain], which increases its “retain count” (number of references); and when you no longer need it, you call [obj release]. When the counter reaches zero, dealloc is then called.
Syntax of dealloc -(void) dealloc
//Message of deallocation.
printf( “Deallocing fractionn” );
[super dealloc];
Commonly used messages in memory management which you send to an abject are:
alloc
new
retain
release
autorelease
copy
Golden Rules of Cocoa memory management:
Retaining the Object: Retaining the object means to increment the retain count of any object and send it the retain message. The retain message increments the object’s reference count, and the release message decrements it.
0 notes