Consequences of a missing init? - objective-c

In the process of porting a really badly coded iOS project to OS X, in which I make a point of preserving the model layer in order to (later) being able to keep the two versions in sync.
I do not currently have access to change the iOS code base - and don't particularly want to, either. Also, for all its faults, the model layer is tested and working.
If it ain't broke don't fix it, they say. So I guess my question is, is the code below broke or not? Notice there is no call to init after the alloc, and the class being instantiated is a direct subclass of NSObject.
...
SuspectClass *obj = [SuspectClass alloc];
obj.arrayProperty = [NSArray arrayWith...];
// etc.
...
I guess another way to put the question is if NSObject's init actually adds anything to an object?

From the documentation of init in NSObject comes the official answer: "An object isn’t ready to be used until it has been initialized."
...and the practical answer: "The init method defined in the NSObject class does no initialization; it simply returns self."
:-)
Though functional, I don't think I'd trust a bare alloc given the number of places that warn that some form of init is required.

without init you get an "object" (at least enough space in memory for the 'object') in an undefined state. You cannot know what happens to that place in your memory, so init is a must

Related

Objective-C init quirks

In a class interface I define some ivar
#property (strong,nonatomic) id < Protocol > initEst; // Initial estimate
This compiles without a problem but when I run the program it crashes with EXC_BAD_ACCESS and [Object .cxx_destruct] indicated by the debugger as the reason.
What is going on?
It's all about the rules of ARC automatic memory management. An initializer has special rules for how it treats the returned value: it retains and returns. See https://clang.llvm.org/docs/AutomaticReferenceCounting.html#semantics-of-init.
Objective-C in general, and ARC in particular, have some pretty strict rules about what names of methods mean. initXXX means "this is an initializer". If this isn't an initializer, don't use the init prefix.
You could turn off ARC entirely and manage memory yourself, but it's easier just to obey the conventions, and it fits better in case of interaction with other languages (such as Swift).
I have tested this some more and there seems to be three conditions for this particular quirk to show up.
In my particular case the ivar's Protocol was also the same as that of the containing class. This seems to be an additional requirement for this problem to surface (refering here to my earlier answer that did not mention this).
So to elaborate on my earlier answer. If
initXXX is an ivar
of id type
that implements a Protocol that is the same as the containing class
then the Objective-C + ARC compiler will happily compile the code but be unable to execute it.
Here is a sample of the code I used to test
#interface Dog : NSObject < Animal >
#property (nonatomic,strong) id < Animal > initState;
#end
Something like this will cause problems simply because the name starts with init. Change the name and all problems disappear.
For reference, the runtime error this generates is
Dog object overreleased while already deallocating
This snippet is pretty abstract but this may bite you in places where you need to specify some initial condition and where it is natural to name some ivar initXxx but beware, if you use Objective-C you do not have that luxury nor will the compiler warn you that it is wrong.
The original error seemed memory allocation related and caused me to suspect the way I used the autoreleasepool but now I am fairly convinced this has nothing to do with the issue.

How to set a breakpoint on "objectAtIndex:" method of a specific property in a specific class in XCode 4?

I would like to set a symbolic breakpoint on "objectAtIndex:" method of a specific property in a specific class.
See the following code :
#interface Foo
...
#property (strong,nonatomic) NSMutableArray *fooArray;
...
#end
I 've tried the following things:
-[[Foo fooArray] objectAtIndex:]
-[Foo::fooArray objectAtIndex:]
-[Foo::fooArray objectAtIndex]
Foo::fooArray::objectAtIndex:
Foo::fooArray::objectAtIndex
Foo::fooArray::objectAtIndex()
None of theses solutions work.
Any ideas to do the trick ?
Unfortunately, while this would be useful, it cannot work, for multiple reasons.
The first involves how methods are specified. A method signature for identifying a method in a breakpoint has three parts:
-¹[NSDictionary² objectForKey:³]
+¹[NSString² stringWithContentsOfURL:encoding:error:³]
Is this an instance method (-) or a class method (+)?
Which class's implementation of this method?
What's the selector of this method?
So your first problem is that what you have written for #2 is not a class.
What you do have is a class, followed in some fashion by a property name. This cannot work, because the debugger has no way to know whether that is a pure accessor—it cannot be sure that you, or whoever implemented that property, didn't write a custom accessor that does something else. This means that the debugger has no good, reliable way to obtain that value, or to know when that value changes, without potentially incurring side effects.
Moreover, the role of a class in a method signature is to identify which class provides the implementation you're setting a breakpoint on. That goes out the window as soon as you start trying to refer to a property that holds an object instead, because the debugger needs a class, and will have to get it from the object—and see the previous paragraph for some of the difficulties of knowing which object that is at all times.
(To be fair, it would indeed be possible for the debugger to watch the value of an instance variable—IIRC, both debuggers can already do this in a watchpoint, though reliability of watchpoints was flaky the last time I tried one. If the debugger could translate the property into its backing ivar, if it has one, and watch that, it would be a decent 90% solution for the majority of properties, which aren't backed by imaginative storage implementations and custom accessors. But the debuggers cannot do this today.)
The second reason is that NSArray is a class cluster.
You probably already know the first part of this (I suspect it's why you're trying to specify a single object by a property of another):
NSArray and NSMutableArray are both abstract classes, which in turn means that neither one implements the business of being an array; each one implements a bunch of convenience methods, while leaving a select set of core methods unimplemented, for subclasses to implement.
So, when you create an NSArray, you do not create an NSArray. The object you get back will be an instance of some private subclass of NSArray, with its own implementation of all of the details of how it manages an ordered list of objects.
So you could set a breakpoint on, say, -[NSArray objectAtIndex:], but it would never get hit, because nothing uses NSArray's implementation of objectAtIndex:—it would not make sense to use that implementation, because that implementation raises an exception (intended to catch subclasses that forget to implement it).
The part that breaks your question is:
While NSArray's implementations of various non-essential methods are defined ultimately in terms of the core methods, such as objectAtIndex:, that does not mean that subclasses are bound to use those implementations. A subclass could very well have its own implementations that don't use objectAtIndex:, if objectAtIndex: is not the most efficient way to do what they do (e.g., if the array is backed by a linked list rather than a C array).
So, to summarize this long answer:
It is not possible for the debugger to reliably watch the value of a property.
As such, it is not possible for the debugger to break when a method in the class of the object that is the value of that property is called, because the correct method to set the breakpoint on may change at any time, and the debugger cannot know when that happens.
Even if you could break on objectAtIndex: of some object identified by property, the array may validly never use objectAtIndex:, in which case your breakpoint would never get hit anyway.
You probably should ask another question about whatever you're trying to do by breaking on objectAtIndex:. I assume you're trying to investigate a bug in your app; that bug is probably another interesting question.
After some digging, I found a way to work this out. That's kinda ugly.
It involves creating a conditional breakpoint dynamically, in a command triggered by a first breakpoint.
First, break whenever your fooArray is ready. I settled on the fooArray accessor, but it could be done earlier :
breakpoint set --name "-[Foo fooArray]"
Then, what you want is break when objectAtIndex: is called on this specific array object. First let's put its pointer in a variable :
expr id $watch = self->_fooArray
and then create a new breakpoint, using this variable in the condition :
breakpoint set --name "-[__NSArrayI objectAtIndex:]" --condition "$rdi == $watch"
$rdi contains self, at least on x86_64. Use $r0 on ARM. (See Clark Cox's great post on the topic.)
-[NSArray objectAtIndex:] is never called. As Peter mentioned, NSArray is a class cluster, and your array is actually an __NSArrayI.
Or, in Xcode :
(Don't forget to check the "continue" box.)
It's not really beautiful, but it seems to work !
I am not at my Mac, so I cannot try this myself, but how about:
breakpoint set -n "-[[Foo fooArray] objectAtIndex:]"

Using a custom allocator in an iOS library

I'm creating a library that will be used by multiple types of iOS apps. Part of my API allows a user to specify routines that will be used for the library's allocations. My library is implemented mostly in C++, so this has been straightforward so far.
However, I've recently been adding some user interface functionality to the library: displaying a UIWebView using a custom view controller. I'm not sure how to ensure that my allocators are used for these objects.
How can I ensure that all of the Cocoa UI objects created by my library are allocated with my own functions?
I've tried a few things including overriding -initWithZone and calling CFAllocatorSetDefault before my -init. None of them have worked yet; and honestly I'm still a beginner with Objective C and Cocoa, so I'd like to know what the "correct" way to do this is.
I'm unable to find evidence of it now, but it certainly was the case that CFAllocator, malloc_zone_t and NSZone were all toll-free bridged. So you could just cast your allocator to an NSZone and pass it along.
I think the problem you're going to face is that NSZone was added at NextStep so as to allow a program to maintain multiple heaps, with the feeling being that it would allow programmers to keep related objects close to one another in memory — which is good for caching — and in some cases to throw away entire object graphs without walking the graph, which is obviously fast. However the former was of little benefit in practice and the latter is more likely to create problems than to be of actual benefit. So Apple has back-peddled from NSZones, gradually turning the related runtime calls into no-ops and removing detailed documentation. Apple's feeling is that, at the Objective-C level, you should not only maintain only a single heap (which is a side issue from your point of view) but that they'll always know best how to maintain it.
EDIT: an alternative idea is to replace NSObject's alloc, that being the thing that creates memory. The Objective-C runtime is well-defined enough that we know exactly what behaviour alloc exhibits, so that a vanilla version might be:
+ (id)alloc
{
Class *newInstance;
// we'll use calloc to ensure we get correct initial behaviour of
// everything equal to 0, and use the runtime's class_getInstanceSize
// so that we're allocating the correct amount of memory irrespective
// of whether this call has fallen through from a subclass
newInstance = (Class *)calloc(1, class_getInstanceSize(self));
// the thing that defines a class is that the first thing stored in
// it is the isa pointer, which points to the metaclass. So we'll
// set the isa pointer appropriately
*newInstance = self;
return (id)newInstance;
}
To replace NSObject's normal init, you'd probably want to define your alternative as a category method on NSObject named, say, customInit, then use class_getClassMethod, class_getMethodImplementation and method_setImplementation directly on [NSObject class] to switch it into place. See the Object-C Runtime Reference for documentation on those.

Override a method in a single object instance

Am not sure how to put this, and I couldn't find the answer because of my inability to find the words to express what am looking for. (!)
In Java, I used to do something like this (I don't remember):
JPanel myButton = new JPanel("Press me"){
public void add(JComponent component){
//override add method
}
};
But, i couldn't find how to do this in Objective-C .. What I found in my search was categories and weird ^{} symbols ...
So, how can I override method(s) in a newly created object?
(For example, override -(BOOL)isEqual; in a newly created NSString* ?)
Am sorry if the question is a bit vague..
EDIT:
Obviously, without subclassing :)
EDIT:
Might as well post my problem in case someone has a better idea:
I have a few CCTransitions in COCOS2D, and I want to be notified when the transition ends .. The thing is, as soon as the transition ends, the -(void)finish; method is invoked (which is part of the CCTransition class structure)
I would really want to avoid subclassing the CCTransition class, and override the finish method to do my logic when the transition ends :)
EDIT:
-(void)onEnterTransitionDidFinish; ... I can't believe something as awesome as that existed and I haven't came across it while searching......
Which means, instead of subclassing CCTransition, override this method in my CCNode subclass :D!
It's still not going to be very clean, but assuming you're willing to concentrate the ugliness, you could do something like (untested):
Method methodToReplace =
[targetClass instanceMethodSignatureForSelector:#selector(methodToReplace)];
IMP implementationToSet =
[someProxyClass instanceMethodForSelector:#selector(implementationYouWant)];
method_setImplementation(methodToReplace, implementationToSet);
Relevant reference documentation is the Objective-C Runtime Reference and, optionally, the NSObject Class Reference (because it makes a few things slightly neater, though e.g. you could use class_getInstanceMethod from the runtime rather than instanceMethodSigntureForSelector:).
Note that you'll have no way to call the original implementation if you use exactly that recipe. method_setImplementation returns the old implementation, it's generally wise to add that to the class under a brand new selector and call that instead.
For reference, I've had a legitimate reason to do this sort of thing only exactly once: when we implemented printing support in an iOS application with which needed to be compatible with both OS 3.2 and 4.0. You need to subclass a particular class, but the class isn't available in 3.2. So you sort of have to subclass at runtime (though the conceptually neater way would be to use a normal subclass, put that into a framework and weak link, but Apple's iOS SDK terms allow static libraries only, so...).
Following Daniel's suggestion, you can implement a method in an NSObject category of the form
[anObject overrideMethod:#selector(foo:)
byBlock:^(id self,id super,id originalArg){
...
}];
What you need to do is to
objc_allocateClassPair against self's own class, to create a new temporary class
Turn a block into a function pointer, using e.g. this or this
method_setImplementation to set the new implementation to the temporary class
use object_setClass to self to set the class to the new temporary class
I haven't figured out how to provide super to the block :p
It's believed this is basically how the KVO is done by Apple, see e.g. this discussion.
Read Runtime reference.
What you have there in Java is an anonymous subclass. This is not possible in Objective-C (well, it sort of is but you would have to do some pretty involved contortions with the Obj-C runtime library).
But Objective-C as of iOS 4 or OS X 10.6 has "blocks", which is what the ^{} syntax is for. This is Objective-C's notion of a closure. This isn't going to help you directly if the APIs that you're calling don't support block callbacks, but you may be able to create wrapper classes that use blocks instead of subclassed methods to handle callbacks.
There are many resources for learning about blocks in Objective-C.

Understanding NSManagedObject

In an existing project I have tried to introduce Core Data long after the project was created, so its model is already in place.
I have created the xcdatamodel and added my only class to it.
That class should act as a global storage for objects in my application.
The class properly implement NSManagedObject and I have verified it gets created and saved in context, also retrieved with a fetch result.
The way of saving data in this class is by means of NSMutableArray. But this is just not working. Here's a fragment of this class:
#interface WZMPersistentStore : NSManagedObject<NSCoding> {
NSMutableArray *persistentStorage;
}
#property(nonatomic,retain) NSMutableArray *persistentStorage;
-(void)add:(id)element;
-(void)remove:(id)element;
-(id)objectAtIndex:(NSUInteger)index;
-(NSUInteger)num;
#end
In the implementation I also override the initWithEntity like this:
- (id)initWithEntity:(NSEntityDescription*)entity insertIntoManagedObjectContext:(NSManagedObjectContext*)context {
NSLog(#"init with entity");
[super initWithEntity:entity insertIntoManagedObjectContext:context];
return [self init];
}
The init method only initialize the mutable array, and I can see from the log that it gets properly called by the app delegate when creating entity.
The add method just send message insertObject to persistentStorage.
The questions that come from this:
Am I doing "conceptually" right ? I
mean, is it correct to have instance
variable in managed object and
initialize like I did ?
when ns logging the size of the
persistentStorage I always get 0
even when logging a moment after the
addObject message (edit: that's not
true, I have verified again and I
correctly got 1 added).
The object stored in managed object
class trough persistentStorage are
normal class with attribute. Is
there something I need to do with
them ? I suppose not because I am
not getting any error at runtime.
No, that is not the "right" approach. You can perform initialization of instance variables in awakeFromFetch. Apple guidelines for NSManagedObject subclasses include the following:
You are also discouraged from
overriding
initWithEntity:insertIntoManagedObjectContext:,
dealloc, or finalize. Changing values
in the
initWithEntity:insertIntoManagedObjectContext:
method will not be noticed by the
context and if you are not careful,
those changes may not be saved. Most
initialization customization should be
performed in one of the awake…
methods. If you do override
initWithEntity:insertIntoManagedObjectContext:,
you must make sure you adhere to the
requirements set out in the method
description [...] (NSManagedObject Class Reference)
To really help, I'd need a deeper understanding of what you're trying to accomplish. Regardless, I strongly suggest combing through Apple's Core Data Programming Guide and sample code before proceeding.
I finally manage to solve this issue. Even if I am a newbie in objective-c, I think that introducing core data after the project is done, is not a good idea. Even if many claim it's easy. Unfortunately, all the people saying so, are showing as proof some really simple tutorial of one entity with one string attribute to change.
Instead for my project I ended up writing much code in addition to the existing one, plus some subclassing (NSManagedObject for example) which break the original model. This added code has also to be written carefully. Derived problem can be as simple as an attribute not saved, or dangerous as deleting wrong entities.
Infact, my problem was due to a wrong configuration in decode and encode method in the classes involved in the process of serialization.
For my questions:
-Point one still remain unanswered, because I am not yet confident in objective-c
-Point two, as I said the related object had some problem with encode/code.
-Point three, I was wrong, there's a lot of code to write, depending how complex is the relevant class.