OK, I have a custom object (an NSManagedObject subclass, if it matters) and I want to pass a pointer to one of its iVars to a function that I've set up to modify such values. With a normal pointer you'd just prefix it with an ampersand (&) as in the classic NSError &error example, but that can't be done with dot notation. I can't just pass &object.iVar as I'd hoped. Can anyone suggest a simple and elegant way to obtain the pointer of iVar so that I can pass it? I am loath to pass the entire object for reasons of code structure and neatness.
-Ash
Argh, as is almost always the case, I ask a question after an hour of frustrating puzzling then ten minutes later answer it myself. I don't know, maybe asking questions is some kind of therapeutic trigger for answers... shame this isn't a psychology website.
Anyway, my solution was to add a new 'pseudo-getter' method to the object I'm trying to access the pointer from that looks a bit like this:
- (Pointer **)getIVarPointer
{
return &iVar;
}
It's a bit cludgy, but since I only have that one iVar whose pointer I need to obtain it's not too bad. On ther other hand if there is a simpler, more 'official' way of doing this, I'd love to know it!
Related
According to the Apple guideline , seems it is confusing, e.g. for method viewWithTag
In Java, I would have a method called
getViewByTag // Java version, equivalent to viewWithTag in Obj-C
But I also found there are some method like objectForKey, so why not just use objectWithKey instead?
getObjectByKey or just get // Java version, equivalent to objectForKey,
// but why not objectWithKey? Or not viewForKey above?
I actually think it is much simpler than what most answers think. I think it has less to do with complex programming language specifics, and has more to do with the uniqueness of the object in question.
When you say viewWithTag:, you are asking the UIView for any view that has that tag. There might be several. The UIView will return one of 'em.
However, objectForKey: (to me) sounds like there should be a single object (or nil) associated with that key. So, the key kinda exists and is bound (tightly coupled) to a single object.
EDIT:
There is an answer mentioning the existence of "by", which further implies how the convention has nothing to do with programming language complexities. It's just natural English.
NSString's stringByAppendingString:, for example, uses by, only because the function is written with a the verb appending. You can't say withAppending, that's bad English.
From my observation
While setting/getting the objects, you use WITH.
e.g. For setting of NSMutableArray object
- (id)initWithCapacity:(NSUInteger)numItems
While setting/getting the properties for objects, you use FOR.
e.g.For setting value for property of type NSMutableDictionary
- (void)setValue:(id)value forKey:(NSString *)key
Hope this helps in clearing your doubt
It seems like with is used for properties that directly belongs to an object. A UIView has a tag property so viewWithTag:14 could be rephrased as "Find the view whose tag property is 14".
When you put an object in a dictionary, associated to a key, this key is not necessarily part of the object itself. objectForKey:#"foo" is a way to say "Look for an object that's linked to the key "foo".
The Apple guidelines do not make any such claims as to when to use for or with. The point of the coding convention is to indicate what the types of the arguments may be and to write method signatures which are natural sounding.
The reason for the for or with in the method name is to identify the type or purpose of the first parameter to the method, which helps it read better.
Apple itself uses several conventions but that is the basic purpose, there is no concrete right or wrong just try to identify the first parameter of the method in the method name with either for or with.
And as for Apple conventions - get(whatever) is not even part of the actual conventions, so you could ask when do I use get or not.
Read this http://cocoadevcentral.com/articles/000082.php
also dont forget (by) NSURL urlByAppendingPathComponent etc - get a feel for it and you wont go wrong
You use "with" whenever the parameter is owned or to be owned, or will be a relatively permanent attribute by the object to the left of the "with" word.
initWithCapacity / arrayWithCapacity - the capacity will be an attribute of the container object being called and will be relatively permanent (until you add objects to it beyond the initial capacity)
viewWithTag – return a view "having" the specified "tag" attribute.
Whereas you use "for" to denote a looser association between the "desired" object and a "token" object that you use to get to it.
- objectForKey / attributeForValue – usually the "object" does not own the key.
- documentForWindow – the window is owned by the window controller and not the document. Furthermore there could be more than one window for each document.
But for looser associations of multiple
objects of the same type within a single method call, it's customary that you use something like:
doSomethingForFoo:withThisOtherFoo:havingYetAnotherFoo:
So the order for such complex call is:
for
with
having
Sorry if this is a noob question, but I get tripped up on some simple things as I'm starting to learn to program. If I use a NSDateFormatter in one of my ViewControllers to display the date I want in a certain format, and the format doesn't change, is it "best practices" to declare that NSDateFormatter as an ivar of my class or property of my class and initialize it in the viewDidLoad so I can reuse it in my different methods? Right now I just instantiate one whenever I need it in a method that deals with the display and was wondering if it's better to just keep declaring it, or declare it once and just use it throughout the viewController class. thanks!
It's not a "noob question"; it's a reasonable question with a reasonable answer.
From a pure efficiency standpoint, you're optimizing prematurely. Don't worry, be happy. If you're managing memory correctly (so that you're not leaking NSDateFormatters) it really shouldn't matter how you obtain a particular instance, especially given that this is such a lightweight object. If you're creating a new NSDateFormatter 1000 times in a loop, you're being silly, but if you're creating the object now and then when you need it and it's going back out of existence properly, it's no big deal.
On the other hand, from an architectural standpoint, make sure what you are doing is DRY. If the date formatter needs any configuration at all, don't repeat yourself each time you create it; write a method that supplies the date formatter the same way each time so that the configuration code is all in just one place.
Instantiating new objects always comes at the price of memory and computing time, so if it's possible without much effort you should try to declare it once and then reuse it, at least within the same class. NSDateFormatter is not thread-safe, though, so you can't simply share one instance between different threads.
For any object, best practice is to just declare it at run time, use it and release it, so that the time taken by that object in memory remains maximum.
We make an ivar when we need the value of that ivar same throughout the class, like any age for an employee, it will be checked in different methods as in seniority, in ageWise category etc. And further if this iVar is an object, then in iPhone, it is a good practice always to be use it as property, so that the memory management problem shifts towards the compiler end.
Now in your case, you may generate a simple method that will take the date object and return the string, or any String as parameter and return date object, make an NSDateFormater there and use it. No need to make it as iVar.
One part of the program takes text from a uitextfield, copies it to a mutable string and then performs
sharedManager.ce_name=name.text
[sharedManager.ce_name replaceOccurrencesOfString:#" " withString:#"%20"
options:NSLiteralSearch range:NSMakeRange(0, [sharedManager.ce_name length])];
At this point it always gave me "attempt to mutate immutable object" - it was not random
The first time I got this error I changed it to
sharedManager.ce_name=(NSMutableString *)name.text
This STILL gave me the attempt to mutate immutable object error, but it would occur randomly - weird right?
I then changed it to
NSMutableString *mutable_name = [NSMutableString stringWithString:name.text];
sharedManager.ce_name=mutable_name;
It has yet to fail on me doing it this way but I am convinced that I have not found the solution.
my questions:
1) Could that fact that it was doing it randomly after the first fix indicate I have some deep seated memory management problem?
2) Why didn't the C-style cast fix it?
3) Will my current fix work?
Thanks for your time :)
The problem here is the way in which you're using casting. When you cast a pointer, it just starts treating the memory at that location as though it were a block of data representing that type.
So if I've got a pointer to a Car class: Car* mycar;, and I cast it to a Person object: (Person*)mycar;, the program will try to access the memory there as though it was pointing at a Person object. However, a car is not a person, except in an old TV sitcom, so when it tries to access members on this or call functions, it's going to try to go to a memory location that contains something other than what it's expecting, and undefined things happen (generally, crashing).
NSMutableString and NSString are incompatible in this regard, and so casting from one to the other will result in terrible times. Your fix ([NSMutableString stringWithString:]) is the correct solution.
If it was doing it randomly, it means that name.text was sometimes a mutable string and some times an immutable string.
Casting between objects like that doesn't change the class of the object. It won't make your immutable object mutable.
That "fix" is probably the best way to do it (at least from what I can see in the code you are showing)
Without seeing at least the declarations of the variables involved, it's difficult to say for certain, but your final solution, creating a new mutable string is likely the correct fix. As for your questions,
Not memory management per se, but it was probably overwriting something it shouldn't have somewhere.
Casts cannot change the fundamental type of an object. You had (presumably) an NSString and all the casting in the world cannot make it into an NSMutableString.
Like I said, probably, but we'd need to see more code to make sure. It's certainly a much better fix.
The docs say:
The default implementation does not
copy attribute values. If the
attribute value may be mutable and
implements the NSCopying protocol (as
is the case with NSString, for
example), you can copy the value in a
custom accessor to help preserve
encapsulation (for example, in the
case where an instance of
NSMutableString is passed as a value).
So instead of getting into trouble and inconvenience with overwriting accessors in my NSManagedObject subclass, couldn't I simply do something like this?
myManagedObject.firstName = [[firstNameMutableStr copy] autorelease];
This would have the exact same effect, or not? The dynamic implementation would retain that anyways ... so.... why not the easy way?
It's an open question whether having to remember to copy the mutable string every where in code you set the attribute is "the easy way."
With a custom accessor, you just write the copy once then forget about. It copies automatically from that point on.
Just imagine that in thousands of lines of code you forgot to copy just once and developed a subtle bug because that one attribute of the managed object sporadically changed because some other totally unrelated code subsequently changed the mutable string you held only by reference.
I could tell you some stories of weekends lost to debugging because someone took "the easy way."
I know that there are functions in the objective-c runtime that allow you to create objects directly, such as class_createInstance. What I would like to know is if anything actually uses these functions other than the root classes' (NSObject) alloc method. I think things like KVC bindings might, but those aren't present on the iPhone OS (to my knowledge, correct me if I'm wrong), so is there anything that would do this?
In case you're wondering/it matters, I'm looking to allocate the size of an instance in a way that circumvents the objc runtime by declaring no ivars on a class, but overriding the +alloc method and calling class_createInstance(self, numberofbytesofmyivars).
Thanks
EDIT
I think I need to be more specific. I am adding classes to the runtime at runtime, and possibly unload and reload an altered version of the same class. I've worked around most of the issues so far, due to things like class_addMethod, but there's no equivalent for ivars after the class has been registered. The two solutions I can think of are having no actual ivars as far as the runtime is concerned, but overriding alloc to make sure I have enough room for them through extraBytes, or alternatively declaring an ivar which is a pointer to all of my actual ivars, which I can then obviously do whatever I want with. I would prefer to use the former strategy but there are a number of things that can go wrong, like if something allocates an instance of my object without going through my overloaded alloc method. Does anyone know of one of these things?
I'm not sure if you're trying to change the behavior of existing classes, which is not safe, or trying to do something for custom classes you own that are direct subclasses of NSObject, which probably is.
Almost all NSStrings you see in practice are instances of a private subclass, and that subclass allocates space for the string inline with the object. Like, instead of containing a pointer to a char*, the character data comes right after the ivars in the object. The extraBytes parameter in NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone) is there for purposes such as this.
So on the one hand, yes, you can pull tricks like that. On the other, you need to know you're doing it with your stuff. If you try to do something like that with the private subclass of NSString (which is private, so you're only going to interact with through runtime introspection), you're probably going to conflict.
There are a few public cocoa classes that also do stuff like this, so you're best off if your classes inherit directly from NSObject. NSLock is one. The layout in memory for a custom subclass of NSLock looks like { isa, <ivars of NSLock> <ivars of subclass of NSLock> <more NSLock stuff, space reserved using the extraBytes parameter> }.
Also, just for the heck of it, note that +alloc calls +allocWithZone:, and +allocWithZone: is the more common override point.
I'm not sure why you'd want to do what you're suggesting--I don't see any reason you couldn't do it, but according to this post, there's usually no reason to use class_createInstance directly (I don't know of anything that uses it specifically). class_createInstance also doesn't take into account memory zones or other possible optimizations used by alloc. If you're just trying to hide your ivars, there are better ways.
EDIT: I think you're looking for the class_addIvar function, which (as the name suggests) dynamically adds an ivar to a class. It only works with the new runtime, so it won't work on the simulator, but it will work on the iPhone.
EDIT 2: Just to be totally clear (in case it wasn't already), you can definitely rely on allocWithZone always being called. Fundamental Cocoa classes, such as NSString and NSArray, override allocWithZone. class_createInstance is almost never used except at the runtime level, so you don't have to worry about any parts of Cocoa using it on your classes. So the answer to the original question is "no" (or more specifically, objects are sometimes created without alloc, but not without allocWithZone, at least as far as I know).
Well there is nothing technically to stop you from overriding alloc. Just create a method in your class called +alloc. I just can't imagine any reason why you would need to.
Sounds like you are trying too hard to manage memory. Let the OS dynamically allocate memory when you create an object. If you are using too much, the OS will send a notification that you are getting close to the limit. At that point you can dealloc stuff you don't need anymore.
If you need so much memory that you have to use tricks, your implementation may need rethinking at the core level instead of trying to fit your square design into the round hole of the iPhone OS.
Just my opinion based on the info you provided.