I'm using C structs in objc and I've created a function that assembles the structure like the one from the Cocoa API. The things is that this structure is not like NSRect o NSPoint this structure packs objc objects soo I'm seeing a potential memory leak here. Do I need to provide a function to 'release' the structure?
I'am not creating a ISKNewsCategory class because there will be no behavior but Do you think this is a good approach or I should define the class even doe there will be no behavior?
typedef struct ISK_NewsCategory {
NSString *name;
NSString *code
} ISKNewsCategory;
NS_INLINE ISKNewsCategory ISKMakeNewsCategory(NSString *name, NSString *code) {
ISKNewsCategory category;
category.name = [name retain];
category.code = [code retain];
return category;
}
In general you would be much better off creating a simple container class. That way all the memory management is easy and you are able to use the object in the standard Cocoa container classes without mucking around wrapping the struct in an NSValue or whatever.
The only time it might be acceptable to use a struct in this way is if you have extremely performance-critical code where the object overhead might become a problem.
#interface ISKNewsCategory : NSObject
{
NSString *name;
NSString *code;
}
#property (copy) NSString *name;
#property (copy) NSString *code;
#end
#implementation ISKNewsCategory
#synthesize name,code;
- (void)dealloc
{
self.name = nil;
self.code = nil;
[super dealloc];
}
#end
As of 2018 you can now use ObjC pointers in C structs and they are retained while the struct is in memory. https://devstreaming-cdn.apple.com/videos/wwdc/2018/409t8zw7rumablsh/409/409_whats_new_in_llvm.pdf
Anything you retain you must release. However, there is nothing that says you must retain them. If the structure is "owning" the objects, then yes, you should retain them, and then you must release them. If the objects are retained elsewhere, though, you might want to consider weak references where you don't retain the objects.
I hate to create classes with no behavior too. :/ This is a sad aspect of Objective-C: classes are verbose.
You have to remember that structures in C are copied each time they're passed around. Therefore, if your structures retain their objects and you give them to someone else, you automatically end up with an erroneous reference count for the objects in it.
If you plan on passing around your objects at all, I think you should make it a full-fledged class. If you don't, a simple struct will be okay.
As of the need of a "destructor", you should have one. You should always have one if there is cleanup to do for your structure.
I hope this solution will be helpful for you.
typedef struct ISK_NewsCategory {
NSString *name;
NSString *code;
} ISKNewsCategory;
NS_INLINE ISKNewsCategory ISKMakeNewsCategory(NSString *inName, NSString *inCode) {
ISKNewsCategory category;
[category.name autorelease];
category.name = [inName retain];
[category.code autorelease];
category.code = [inCode retain];
return category;
}
Related
Today I was at interview and was asked a question:
Generate setter and getter by hands for proper declaration using manual reference counting:
#interface SomeClass : NSObject
{
NSMutableArray* _array;
}
#property (copy) NSArray* array;
#end
My answer was:
- (NSArray *)array
{
#syncronized (self)
{
return [_array copy];
}
}
- (void)setArray:(NSArray *)array
{
#synchronized (self)
{
if (_array != array)
{
[_array release];
_array = [array mutableCopy];
[_array retain]
}
}
}
I never worked using MRC so not sure about correctness of an answer. Please help me to correct this code with description!
I am the author of one of the linked topics and I think now I understand MRC enough to write this answer here:
1) You're obviously leaking the copy in the getter (see it also in the comments) - so it should be balanced by corresponding autorelease call.
Also note, that this copy inside your getter is done because of you need to return immutable object, not because of getters for #properties declared with (copy) require you to do so!
2) Your setter should not retain after mutableCopy, since mutableCopy already does +1 for you.
See the following quotes from Advanced Memory Management Programming Guide
Basic Memory Management Rules.
You own any object you create
You create an object using a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy” (for example, alloc, newObject, or mutableCopy).
And
Ownership Policy Is Implemented Using Retain Counts
The ownership policy is implemented through reference counting—typically called “retain count” after the retain method. Each object has a retain count.
When you create an object, it has a retain count of 1.
3) In my topic's comments #robmayoff shared the link to open source implementation of runtime: reallySetProperty in objc-accessors.mm with the following reasoning behind it:
The nonatomic retain and copy setters unfortunately have an unnecessary race condition. If, on thread 1, the setter releases _count, and on thread 2 the getter accesses _count before thread 1 has set _count = [count retain], thread 2 may access a deallocated object. Always store the new value in _count before releasing the old value. The real accessor in the Objective-C runtime does it correctly. See reallySetProperty in objc-accessors.mm. – rob mayoff
4) You example is also missing dealloc since you were to write it under MRC.
5) [IMO, maybe subjective] Since your setter is creating copies of array argument, you don't need to have this if (_array != array) check since the task of (copy) setter is, I believe, to produce copies of what is passed, so I think this is may be omitted.
Having these points in mind I would write your example like the following:
- (NSArray *)array
{
id array;
#synchronized (self)
{
array = [_array copy];
}
return [array autorelease];
}
- (void)setArray:(NSArray *)array
{
id oldValue;
#synchronized (self)
{
oldValue = _array;
_array = [array mutableCopy];
}
[oldValue release];
}
- (void)dealloc {
[_array release];
[super dealloc];
}
In answer to your question in the comments:
Is it normal and really can be used in the daily practice?
I would say, that it can be used in a daily practice with the following additional considerations:
1) You should move you ivar declaration into a private category #interface SomeClass () be it inside your .m file or a private class extension.
2) You should make your getters/setters nonatomic since atomicity of this property is on your shoulders (you already do synchronized on your own in both setter and getter).
3) See also the setup from linked topic which omits ivar and uses second #property declaration. In your case it would look like this:
// .h
#interface SomeClass : NSObject
#property (nonatomic, strong, readonly) NSArray *array;
#end
// .m or private class extension
#interface SomeClass()
#property (nonatomic, strong) NSMutableArray *array;
#end
#implementation SomeClass
// and here your getters/setters
#end
This setup looks promising though I haven't really tested it for the case like yours.
P.S. Recently I did some research for this back-to-the-past Manual Reference Counting, let me share with you the following links which I found to be the best on this topic:
Advanced Memory Management Programming Guide (this is the MUST)
An In-depth Look At Manual Memory Management In Objective-C (this one too!)
What clang taught us about Objective-C properties
Memory and thread-safe custom property methods
Source code of objc runtime.
I'm not sure I understood how alloc and retain work.
Recently I discovered that the NSString properties were not retained and I had to add [myString copy] when I set them. Which makes me wonder if I misunderstood the whole way of using retain/alloc
Please, may someone tell me if I'm doing it correctly? I read a lot and had a look on open source projects, this let me thing that I may have been wrong since the beginning.
Here is my way of doing it:
/**** VIEW.h *****/
#import "MyClass.h"
#interface MyViewController : UIViewController {
//Is the following line really necessary?
MyClass *myObject;
}
#property (nonatomic, retain) MyClass *myObject;
- (void)defineObject;
#end
.
/**** VIEW.m *****/
#import "VIEW.h"
#implementation MyViewController
#dynamic myObject;
- (void)viewDidLoad
{
[super viewDidLoad];
[self defineObject];
NSLog(#"My object's name is: %#", myObject.name);
}
- (void)defineObject
{
//Here particularly, Why doesn't it work without both alloc and init
//shouldn't "#property (nonatomic, retain) MyClass *myObject;" have done that already?
myObject = [[MyClass alloc] initPersonalised];
[myObject setName:#"my name"];
}
.
/**** MyClass.h *****/
#interface MyClass : NSObject {
//not sure if this line is still necessary
NSString *name;
}
#property (nonatomic, retain) NSString *name;
- (id)initPersonalised;
- (void)setName:(NSString *)name;
- (NSString *)name;
#end
.
/**** MyClass.m *****/
#import "MyClass.h"
#implementation MyClass
#dynamic name;
(id)initPersonalised{
self = [super init];
name = #"Undefined";
}
- (void)setName:(NSString *)name{
self.name = [name copy];
}
- (NSString *)name{
return [self.name copy];
}
#end
I hope you can bring a bit of light, after months of programming this way, I'm less and less sure of doing it well.
This is indeed a topic that every Objective C programmer stumbles upon. There are a few things one needs to know:
Instance variable vs. property access
Within MyViewController,
myObject = xxx;
and
self.myObject = xxx;
are two different things. The first directly assigns to the instance variable and does neither release to old referenced insance nor retain the newly assigned instance. The latter one uses the property setter and thus releases the old and retains the new value.
Deallocation
Even when you have declared an implemented a property that takes care of retaining and releases the values, it won't take care of deallocation when your object (MyViewController in your case) is released. So you must explicitly release it in dealloc:
-(void) dealloc {
[myObject release];
[super dealloc];
}
Now to your code:
The snippet:
myObject = [[MyClass alloc] initPersonalised];
is perfectly okay. When you create an object, you use the pair of alloc and initXXX. The always create an instance with the reference count set to 1. So by directly assigning it to the instance variable, you create a clean constellation. I don't see no other way of creating the instance.
In MyClass you could use #synthesize name instead of #dynamic. Then the compiler would implement name and setName: automatically and you wouldn't need to do it yourself.
Finally, your missing dealloc.
Update:
If you use:
self.myObject = [[MyClass alloc] initPersonalised];
then you have a memory leak because initPesonalised sets the reference count to 1 and the setter of myObject increases it to two. If you want to use the setter, then I has to be:
MyClass* mo = [[MyClass alloc] initPersonalised];
self.myObject = [[MyClass alloc] initPersonalised];
[mo release];
It would be different if you weren't using initXXX to create a new instance. The class NSString for example has many methods called stringXXX, which create a new instance (or return a shared one) that has (conceptually) a reference count of 1 that will later automatically decreased by one. Then you better use the setter:
self.name = [NSString stringWithFormat: #"instance %d", cnt];
If you want to use copy instead of retain for your string property (which is good practice), then you can simply declare your property like this:
#property (nonatomic, copy) NSString *name;
When you then use #synthesize to implement the getter and setter, the compiler will generate them using copy instead of retain.
And NSString *name; is necessary even if you use #property and/or #synthesize to implement the property.
Alloc and init are methods that always go hand-in-hand. alloc allocates space for your object, and init initializes your object to some value. When you call alloc, you are responsible for freeing that object later. If you call copy, you are also responsible for releasing that object later. It's considered good practice to always initialize your objects right after you allocate them.
Now, to answer the questions I found in your code.
#interface MyViewController : UIViewController {
//Is the following line really necessary?
MyClass *myObject;
}
So is that line necessary? That depends. Does it make sense that your object has a MyClass as a property? This is a question only you can answer based on your design. I recommend you to study Object-Oriented Programming in more depth.
- (void)defineObject
{
//Here particularly, Why doesn't it work without both alloc and init
//shouldn't "#property (nonatomic, retain) MyClass *myObject;" have done that already?
myObject = [[MyClass alloc] initPersonalised];
[myObject setName:#"my name"];
}
Not necessarily. You are just providing a pointer to an object of the specified kind. The moment you set your property, depending on the property modifiers, your class will know what to do with MyObject.
In that way, there's no need to call [yourObject copy]. In this way your properties will be copied instead of being retained. Just don't forget to release it later in your -dealloc method, like you would with retain properties.
All in all, this is what I recommend you to study a bit more:
Object-Oriented Programming (not related to your issue, but I can tell you are not comfortable using it. Objective-C is heavily object oriented, so you want to understand OOP).
iOS Memory Management.
You can have a look at the Memory Management Guide. It will help you to better understand the alloc & retain concepts; hope this helps you.
I am trying to create a custom object that simply inherits the NSString class and overrides the 'description' method.
When I compile, however, I am getting a warning:
Incompatible pointer types initializing 'OverrideTester *' with an expression of type 'NSString *'
Here is my code:
main.m
#import <Foundation/NSObject.h>
#import <Foundation/NSString.h>
#import <Foundation/NSAutoreleasePool.h>
#import "OverrideTester.h"
int main (int argc, char *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString *str = #"Programming is fun";
OverrideTester *strOverride = #"Overriding is fun";
NSLog (#"%#", str);
NSLog (#"%#", strOverride);
[pool drain];
return 0;
}
OverrideTester.h
#import <Foundation/Foundation.h>
#interface OverrideTester : NSString
-(void) description;
#end
OverrideTester.m
#import "OverrideTester.h"
#implementation OverrideTester
-(void) description
{
NSLog(#"DESCRIPTION!\n");
}
#end
NSString is part of a class cluster. You cannot just create arbitrary subclasses of it, and when you do, you can't assign constant strings to them (which are type NXConstantString). See Subclassing Notes in the NSString documentation. Generally you don't want to subclass NSString. There are better solutions for most problems.
you are assigning an instance of NSString to your variable of type OverrideTester. If you want an instance of your class, you need to instantiate an instance of that class; type-casting will never change the class of an instance.
description is defined as returning an NSString*:
- (NSString *)description;
Do not try to learn about subclassing and overriding methods by subclassing NSString (or any other class cluster). If you want to play with subclassing and such -- a very good idea when new to the language, assuredly -- then subclass NSObject, potentially multiple levels , and play there.
How do you mean to subclass NSObject,
potentially multiple levels? Isn't it
possible NSObject might have
conflicting methods compared to other
class clusters or just not have them
available to override?
If your goal is to figure out how method overrides work (which I thought it was), then you'd be better off doing it entirely yourself.
I may have mis-read your question.
In any case, subclassing NSString is pretty much never done. There are very very few cases where it is useful. Overriding description in anything but custom classes specifically for debugging purposes is useful, yes. Calling description in production code should never be done.
Also, why would description return an
NSString* in this code?
What would happen if something that expects an NSString* return value were to call your version that doesn't return anything?
A crash.
You are declaring a variable named strOverride of type pointer to OverrideTester. But to that variable, you are trying to assign a pointer to an NSString. You cannot assign a superclass to a variable of a subclass. Imagine a generic class TwoWheeled and a derived class Motorbike. A Motorbike can be treated like a TwoWheeled, but not the other way round as the Motorbike has features a normal TwoWheeled might not have like a motor.
Totally new to Obj-C, so thanks for patience. :P
Because I'm beginner, I will use the car example. Easier for me to understand.
I have an object, Car. It has two member objects, tire and engine.
Tire and engine have their own member variables too, but they are just int with various names (like pressure, treadDepth).
In all these cases, I have synthesized accessor methods. I'm not sure about accessor methods for objects, so I just did #property id engine / #property id tire. Hope that is right!
Now, I can do dot.notation style to access like: [car.engine cylinders]. Fine! Sending tire and engine messages works fine. I write methods, this notation seems to work.
But when I declare an array of objects like 4 tires for the car:
#interface Car : NSObject {
tire *tires[4];
}
I cannot send it message like this
[car.tire[0] setPressure: int];
It says accessing unknown tires getter method.
Basically I am wondering if someone can help me understand how to correctly access member variables of an object that is in an array.
Thanks!
You are trying to call a getter on car that doesn't exist. You can't return a C-style array by value anyway, so instead of just returning a Tire* pointer i'd rather use a NSArray in this case:
// header:
#interface Car : NSObject {
NSArray *tires;
}
#property (nonatomic, copy) tires;
// ...
// source:
#implementation Car
#synthesize tires;
- (id)init {
if ((self = [super init])) {
tires = [[NSArray alloc] initWithObjects:
[[[Tire alloc] init] autorelease],
// ...
nil];
// ...
}
return self;
}
- (void)dealloc {
[tires release]; // don't forget to clean up
// ...
}
Now you could use the getter:
[[[car.tires] objectAtIndex:0] setPressure:0];
Why not put all of your tire objects into an NSArray or NSSet? Or, since you know there are only four, you could simply define frontLeftTire, frontRightTire, etc. properties.
Well you could use Objective-C style arrays. Then you would have something like:
NSArray *tires = [NSArray arrayWithObjects: tire1, tire2, tire3, tire4];
And then you would access them as:
[tires objectAtIndex:0];
That's assuming you are using the synthesized methods as described. I'm not sure from your question, but it seems like you might want to define a class "tire" for these objects (rather than just a method, which is all I see above) that inherits from NSObject, or maybe from your own class CarPart, etc. Then you allocate 4 tires in a loop and call an init method that sets up some default state (hopefully better than the donut that came as the spare in my car) and then add them to your array in "Car" when you initialize a car.
Ok, so I have the code below (Objective-C FYI) and I was wondering if I want to create an NSMutableArray of c_data objects, how would I go about doing that? It's sort of like declaring a List<c_data> cData in C#.
#interface c_data : NSObject {
double value;
int label;
int ID;
}
#property double value;
#property int label;
#property int ID;
-(c_data*) init;
-(c_data*) initWithValue:(double)value;
#end
#implementation c_data
#synthesize value, label, ID;
-(c_data*) init {
return self;
}
-(c_data*) initWithValue:(double)val {
value = val;
return self;
}
#end
If you look at the class feat_data, I'm trying to make cData an array of the class c_data. I have included my attempts at it, but I don't think it's right because c_data isn't an array. Any suggestions?
#interface feat_data : NSObject {
NSMutableArray *nData;
NSMutableArray *cData;
char type;
}
#property(nonatomic, retain) NSMutableArray *nData;
#property(nonatomic, retain) NSMutableArray *cData;
#property char type;
-(feat_data*)init;
#end
#implementation feat_data
#synthesize nData, cData, type;
-(feat_data*)init {
nData = [[NSMutableArray alloc] init];
c_data *c_dataInstance = [[c_data alloc] init];
cData = [[NSMutableArray alloc] initWithArray:c_dataInstance];
return self;
}
#end
There is no such thing as statically typed / template / generic collections in Objective-C. Basically, the point of a strongly-typed collection is to provide static type safety at compile time. Such an approach makes little sense in a language as dynamic as Objective-C. The approach to the problem of disparate object types in Objective-C collections is to only insert the appropriate object type(s). (Also, remember that the array will retain objects it contains, so if you insert a new object without releasing and you lose the pointer to it, you're leaking memory.)
If you think about it, one of the biggest benefits to generics is being able to retrieve objects from the collection directly into a statically-typed variable without casting. In Objective-C, you can just store to an id variable and send whatever message you like without fretting about a ClassCastException, or the compiler complaining that an object doesn't (may not?) implement the method you're attempting to invoke. You can still statically type variables and cast results if desired, but the easier approach is to use dynamic typing (and -isKindOfClass: and -respondsToSelector: if necessary).
Incidentally, there are several related incarnations of this question on Stack Overflow. Knowing the term(s) to search for ("generic", "strongly-typed", or "template") can help find them. Here are a few:
Why do C# and VB have Generics? What benefit do they provide?
Is there any way to enforce typing on NSArray, NSMutableArray, etc.?
Is there anything like a generic list in Cocoa / Objective-C?
Are there strongly typed collections in Objective-C?
Finally, I agree with William — your init methods are pretty egregious in the sample you provided. You'd do well to learn and heed Apple's rules of Allocating and Initializing Objects in Objective-C. It requires breaking habits from other languages, but it will save you endless hours of insanity at some point down the road. :-)
[NSMutableArray addObject:[[[c_data alloc] init] autorelease]];
Objective-C arrays aren't typed. It seems you have some C++ unlearning to do.
On a related note, your inits are pretty bad. You need to call super init as well, as such:
- (id)init {
self = [super init];
if (self != nil) {
//Initialize here.
}
return self;
}
You would create an NSMutableArray and insert c_data objects into it.