For instance, I subclassed UILabel and added a method or property called -verticalTextAlignment to align text vertically.
And in the future, if next version of SDK or iOS added a method or property which has the same name, then it is possible my app crashs or behaves unexpectedly.
*This problem happens even if you use category instead of subclassing.
Question1
How to avoid this kind of accidental overriding in Objective-C?
I think you can avoid this by prefixing all of your methods and properties like -XXX_verticalTextAlignment. But it is not realistic, is it?
Question2
I know that this kind of accidental overriding happens at compiling time or when you update your iOS SDK, OSX SDK, or XCode.
But is it possible to happen also when updating your iPhone's iOS version?
For example, is it possible that your app runs well on iOS5 but doesn't run on iOS6 due to accidental overriding in iOS6.(You can replace iOS5 and iOS6 with any versions, for example iOS5.0 and iOS5.1)
Yes you could use your own prefix, however that is uncommon.
Yes, it can happen after an O/S update; Objective-C is a very dynamic language where messages are sent to methods rather than being called, as in other languages. These things are worked-out at runtime, not compile time.
Bottom line is yes, you might accidentally override a future base class method and the only solution is to be there when it happens and rename the method.
Prefixing your category methods are the safest way to avoid these conflicts in an every growing, non namespaced cocoa ecosystem.
It's quite valid and reasonable that framework creators, open source developers and other individual developers should prefix their class methods.
Quite often I write methods prefixed with my company initials, then continue with the method.
- (void)JCMyMethodNamedSimilarToBaseClass;
Question 1a Category
Use a prefix, or avoid the whole mess and use functions instead.
Question 1b Subclass Methods
If a method does something general enough that the superclass may also implement it, I simply choose a method name which is a little more 'wordy' than Apple would conventionally choose -- such as specifying a non standard typename in the method's name. Of course, this only reduces the possibility of collision.
If you need a higher level of security, you could just test this at execution (so you know when they are introduced) and hope every user stays up to date -- or you could rely on C and/or C++ more heavily (they do not have this problem).
Question 2
But is it possible to happen also when updating your iPhone's iOS version?
Yes. It's not so unusual. When the frameworks are updated (e.g. via software update), they may contain updated frameworks. This code is loaded into the objc runtime, and you always get the version of the installed frameworks' objc implementations.
It's also a much broader problem on OS X, where you may load code and/or plugins dynamically quite easily.
An elegant solution is to check for the method being already there and only adding it if it isn't: Conditional categories in Mountain Lion
Related
With time I started using the same "framework" I built myself for many projects, but I'm in the process of refactoring a lot of it now. I will have to break the interface of many classes (changing return types, poor naming, functions that modified their input argument, some have nasty side effects...), and my older programs are obviously relying on them.
My question is: how do you make such big changes and keep older code working? Start making "version" folders?
Or is it a bad practice to use classes across projects directly? (e.g. a custom Math class, and everyone accesses the same file)
If you meant actual interfaces:
As far as I know, you should try to avoid changing interfaces as much as possible. They're a contract and as such are set in stone. If you ever want to add or change something, you could overwrite it and add the new method to the subInterface.
In general:
I'd keep all the old methods, but deprecate them and rewire their functionality to your new overloads.
An old post explains pretty well: Why shoudn't I use accessor methods in init methods
But my question is:
Q. If I am not overriding accessor methods in sub class in Objective C, is it safe to use in init?
It is usually avoided to prevent KVO mechanisms from accessing a partially initialised or partially deallocated object. This is particularly true for Mac OS X development where bindings and KVO play a large part especially in an application's user interface. I'm not an iPhone developer but if KVO is used on the iPhone platform as well, then it may be reason enough to avoid using accessor methods in your init and dealloc methods.
KVO makes a dynamic subclass of your class so that it can easily monitor changes to properties.
It's easy enough to avoid them in init and dealloc. Some argue that it's easier to use the accessor methods everywhere regardless of Apple's recommendations, but the convention is to avoid using them for init and dealloc and following the convention usually means less hurt later even if you don't anticipate any problems now.
Best practices from Apple say don't use self. accessors when in init or dealloc methods. It'll probably be fine, but basically Apple can't guarantee it.
If I am not overriding accessor methods in sub class in Objective C, is it safe to use?
Answer: Never user self.accessor.
From my 2+ years working experience under 20+ years apple developers(cocoa,obj-c), what I learnt is same.
Many a times our team has been asked to remove all those and and guided to use some other design pattern or way, even though it was required.
This may put you under problem if the object was created as nonatomic, and many threads working on same property/object. self. makes your class/object bind-ed. As you have tagged ios, for previous versions of ios this could have been done, but now ios are supporing kvo, so you should not follow this way.
So I am currently working on a universal binary for an app that is to be ran on iPad and iPhone. The differences are basically only UI changes.
So, with that in mind, I have a class called ConfigurationController that is of type UIViewController. I then sub class ConfigurationController into iPadConfigurationController and iPhoneConfigurationController.
Now, lots of the code is common, so I abstracted it out to the ConfigurationController. However, there are a few functions in which the changes result in 1 line code differences. Im currently in a debate with other developers about what to do with this code.
There seem to be two paths:
1: push the methods into the ConfigurationController super class, then use macros to determine if the app is being run on an iPad or an iPhone. Then call which ever code needs to be called based on that.
2: keep the methods in the subclasses (iPadConfigurationController and iPhoneConfigurationController) and keep the 1 line changes. This results in about 300 lines of code being duplicated. However, this leaves a path for the future, as in if there is a change now, there can be change after further development. In which case, I would already have the functions in the subclasses.
Which would be more beneficial and have less overhead?
Refactor the 1-line differences into small helper functions, declared in the base class, but implemented in the derived classes (I'm more a C++ guy, so I hope the terminology makes sense).
I'm currently developing iPhone applications that -- for the time being -- need to be supported on versions prior to 4.x.
When I want to use something 4.x or 3.2 specific, I would use the usual approach of checking if a given class exists, i.e.:
if(NSClassFromString(#"SomeClass")) {
// do stuff here
}
This mostly works, except when it doesn't.
Let me explain:
Classes like:
UIMoviePlayerViewController (only available on 3.2+ according to the documentation)
UIGestureRecognizer subclasses (like UIPanGestureRecognizer; UITapGestureRecognizer, etc.)
Actually resolve to valid classes, as if they were already available on private APIs on earlier versions.
Is this the case, or am I missing something?
This is really bad, since the NSClassFromString() approach is actually recommended by Apple.
To be on the safe side, I'm currently checking the OS version instead of doing runtime checking for the classes existence.
Anyone else having this problem?
Thanks.
Nope, you are not doing anything wrong, there are a number of private classes that were made public in 3.2/4.0. This would not be a big deal if they were just exposing existing functionality, but often the classes also have significantly altered functionality.
The only 100% viable thing to do is to test the version, even though that is generally frowned upon. Even testing for specific selectors may not be sufficient, since there is no guarantee that the selectors on old versions behave the same as the selectors on the version where the object was made public.
Checking the OS version is even better than using NSClassFromString(...) since some classes already exist in versions prior to 4.0/3.2 (as you mention). You can just use this way :)
For MPMoviePlayerViewController, I use [MPMoviePlayerController instancesRespondToSelector:#selector(view)]. They were added at the same time and are part of the same framework.
Sometimes Apple documents this; e.g. for UINib I think you're supposed to check [UINib respondsToSelector:#selector(nibWithNibName:bundle:)]. I can't remember where I read this.
I haven't actually come across the recommendation to use NSClassFromString(). I usually use something like [AVCaptureSession class], but you can equally just use [[[AVCaptureSession alloc] init...] autorelease]. Nonexistent weak-linked symbols become NULL at load time; this means the AVCaptureSession Class-object becomes Nil, and methods on nil do nothing and return nil.
If you're going to compare version numbers, a somewhat more reliable way is to compare against kCFCoreFoundationVersionNumber. I don't see one for 4.0 yet; kCFCoreFoundationVersionNumber_iPhoneOS_3_2 appears to be the highest one in the 4.0 headers.
I did some searches and found that if want to adjust the brightness, I need to use private framework and apple will reject it.
If I just want to get the value of brightness(just get that value to display, not adjust it), is there a way which apple accept?
Using a undocumented API of a public framework is not necessarily bad. I know many people, including myself who use undocumented methods here and there, for example to check for connectivity or add text fields to UIAlertViews.
On the other hand, linking to a private framework is much worse, because those tend to be very volatile, and might be removed from 3.0, or renamed.
I'm not sure about the specifics of your case, but the distinction between private framework or undocumented APIs of a public framework is important.
There is no way to answer the question "will Apple allow it?" You have to pick your risk tolerance and submit. Personally, I stay away from things that are undocumented. But in the cases that I do enhance my programs with things on the edge (generally undocumented things that are not actually private), I try to make my program resilient to changes.
One major way to do this is to check whether the thing you're about to link to or function you're about to call exists. For methods, you can use -respondsToSelector: before calling it. For functions, you can test whether the function exists:
extern void SomeFunction() __attribute__((weak_import));
if (SomeFunction != NULL) {
SomeFunction();
}
(You'll need to test that out on iPhone; I use it on Mac, and weak_import should be portable since iPhone uses GCC4. See Ensuring Backward Compatibility. So far, I've always been able to find some trick to avoid actually needing this on iPhone.)
The key to all of this is to make sure your program functions without the undocumented feature. That way, even if Apple rejects it, pulling it out is trivial.
That said, for me this is a last resort for things I've spent a lot of time trying to do in a documented way, for things that provide a significant improvement to the user experience, and don't violate Apple's basic intent. For instance, running in the background, even if you can get it to work, is a clear violation of the spirit of Apple's rules; other things are private just because they haven't been made public, like the internal views of UI elements.
Changing the screen brightness in a way that persists outside your app would seem a violation of the basic rules of play (stay in your sandbox). Getting the current screen brightness doesn't sound like that (though I'm not sure what you would do with it.)