I am having some difficulty with key-value-observing logic in conjunction with XCTest (the original code is being retrofitted with test coverage). The logic works fine in the normal (non-test) context, but blows out with an exception every time in the context of a test.
The gist of the logic is this -- I have two classes, call them Service and Helper. Scaffold implementations are:
interface Service : NSObject {
BOOL svcCallComplete;
}
#end
#implementation Service
- (id) init {
if ((self=[super init])==nil) {
return nil;
}
return self;
}
#end
interface Helper : NSObject {
}
#end
#implementation Helper
- (id) init {
if ((self=[super init])==nil) {
return nil;
}
return self;
}
#end
Helper is an observer of an attribute in Service. In the context of my normal runtime logic I do this with a call to a Service instance method addSvcObserver:
Service.m:
- (void) addSvcObserver:(id)observer {
[self addObserver:observer
forKeyPath:#"svcCallComplete"
options:NSKeyValueObservingOptionNew
context:nil];
}
Helper complies with the KVO observing pattern thus:
Helper.m:
- (void) observeValueForKeyPath:(NSString*)keyPath
ofObject:(id)object
change:(NSDictionary*)change
context:(void*)context {
}
Pretty straight-forward, and I won't go into the logic for monitoring the attribute change as the problem occurs way before that -- if I have a code excerpt like:
Service* service = [[Service alloc] init];
Helper* helper = [[Helper alloc] init];
[service addSvcObserver:helper];
there are no problems in the non-test case (i.e., this and associated KVO logic works as expected). However the addSvcObserver call when performed in the context of an XCTest test method produces an immediate access denied exception.
I've included an exception "break on all" breakpoint -- the problem seems to be occurring in objc_registerClassPair during the addObserver:forKeyPath:options:context: call. The test target has ARC explicitly disabled as the project for which it is providing test coverage (the target is iOS7) is non-ARC for legacy reasons; this does not seem to cause any problems with other tests.
Thoughts?
interface Service : NSObject {
}
#property (nonatomic) BOOL svcCallComplete;
You should declare svcCallComplete as a property.
because The observed class must be key-value observing compliant for the property that you wish to observe
The reason you get the objc_registerClassPair i think maybe because KVO dynamic register a subclass of your Service , but could not find a setter method of svcCallComplete.which the dynamic subclass needs to override that setter method and send notification.
For more detail read this.
The cause of this turned out to be that my implementation of the KVO logic was incomplete. According to the guide here, you must override the NSObject implementation of automaticallyNotifiesObserversForKey: when using manual change notification -- I somehow missed that in my initial read of the text. I added the following to my Service class:
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
BOOL automatic = NO;
if ([theKey isEqualToString:#"svcCallComplete"]) {
automatic = NO;
} else {
automatic = [super automaticallyNotifiesObserversForKey:theKey];
}
return automatic;
}
and all is now correct in the test case. Anyone care to hazard a guess as to why this was not blowing out in the normal (non-test) case?
Related
I am trying to add a condition to NSObject's description method where any object that responds to a method in an optional protocol (PrettyPrinter) will print the result from the protocol method instead of the normal NSObject's description. However, if the object being printer does not respond to the protocol then the description should return how it normally does.
My current attempt at doing this involves writing a category on NSObject that contains this protocol and the overridden description method. However, I am unaware of any way to call a category's un-overridden method.
-(NSString*)description
{
if ([self respondsToSelector:#selector(prettyPrinter)]) {
return self.prettyPrinter;
}else{
// Would Like to return normal description if does not respond to selector. But
// can not call super description due to being a category instead of a subclass
return [super description];
}
}
Any ideas on ways that I can accomplish this would be greatly appreciated. Thanks!
UPDATE:
With a little more searching, it would seem like this may be able to be accomplished through something called swizzling. However, current attempts at this have not yet been successful. Any advise on techniques to use swizzling to accomplish this goal would also be helpful.
As you point out, this is possible with method swizzling. The runtime has functionality to exchange the implementation of two methods.
#import <objc/runtime.h>
#interface NSObject (PrettyPrinter)
- (NSString*) prettyPrinter;
#end
#implementation NSObject (PrettyPrinter)
// This is called when the category is being added
+ (void) load {
Method method1 = class_getInstanceMethod(self, #selector(description));
Method method2 = class_getInstanceMethod(self, #selector(swizzledDescription));
// this is what switches the two methods
method_exchangeImplementations(method1, method2);
}
// This is what will be executed in place of -description
- (NSString*) swizzledDescription {
if( [self respondsToSelector:#selector(prettyPrinter)] ) {
return [self prettyPrinter];
}
else {
return [self swizzledDescription];
// The above is not a recursive call, remember the implementation has
// been exchanged, this will really execute -description
}
}
- (NSString*) prettyPrinter {
return #"swizzled description";
}
#end
The -prettyPrinter method can be removed, in which case NSLog will return the value defined by -description.
Note that this will only swizzle -description, although NSLog might call other methods, such as NSArray's –descriptionWithLocale:
if a category overrides a method that exists in the category's class, there is no way to invoke the original implementation.
Click Here To Read More!
Looking at the Objective-C runtime library source code, particularly at objc-runtime-new.mm, I saw some functions and even comments which referred to lazy and non-lazy classes. It seems that classes that don't have a +load method are called lazy classes, but I'm not sure of that and most likely that is not right. After searching on Google, I didn't found anything about lazy classes on Objective-C.
So, what is a lazy class in Objective-C? Does Obj-C have this feature? Is it related to the presence of a +load method in a class' implementation? At the file linked above, the runtime system calls a function called _getObjc2NonlazyClassList in order to get a list of non-lazy classes from an image. Why isn't there a _getObjc2LazyClassList function too?
I found the answer: It's all about a class implementing or not a +load method.
All the classes implemented in a given image file have a reference in a list stored in the "__DATA, __objc_classlist, regular, no_dead_strip" binary's section. This list allows the runtime system to keep track of all the classes stored in such file. However, not all of the classes need to be realized when the program starts up. That's why when a class implements a +load method, it also has a reference in a list stored in the "__DATA, __objc_nlclslist, regular, no_dead_strip" section.
So, _getObjc2NonlazyClassList retrieves the list of classes that do implement a +load method and are so called non-lazy. _getObjc2ClassList retrieves a list of all the classes in a image file, including the classes that don't have a +load method (and are called lazy) and the non-lazy ones. Non-lazy classes must be realized when the program starts up. Lazy classes, on the other hand, don't need to be realized immediately. This may be delayed until the class receives a message for the first time, for example (that's the reason for them to be considered "lazy").
The same is true for categories, by the way.
"Lazy" is used in two different contexts.
The first, when critiquing a class design, argues that a class is ineffectual -- that it doesn't do enough to justify its existence. People also call this kind of class "thin." This is probably not what you mean here.
Second, lazy evaluation and lazy instantiation mean that the class only does the work of evaluating a property or initializing itself when actually needed.
For example, suppose we have a class that makes an Employee object.
#implementation Employee
- (id) initWithID: (IdentificationCode*) ident
{
self =[super init]
if (self) {
_records=[self retrieveEmployeeRecordsFor: ident];
_identification=ident;
}
return self;
}
This is fine, but retrieving all the records from a database might be slow. And sometimes we don't need to do the work. For example:
- (BOOL) isFounder
{
if (indent.number<10) return YES;
return NO;
}
If we're instantiating an Employee simply to find out if they're a Founder, we don't need to look up their records at all!
.....
if ([thisEmployee isFounder]) {
[self sendCandyTo: thisEmployee.identification];
}
On the other hand, sometimes we need them:
- (NSArray*) payments
{
return [self.records retrievePayStubs];
}
So, if we're constructing an Employee just to call isFounder, we waste a database lookup. But we can't just skip that, because payments needs it.
What we do is take the database lookup out of the constructor and put it in a load method.
- (void) load
{
if (records) return;
self.records=[self retrieveEmployeeRecordsFor: ident];
}
- (NSArray*) payments
{
[self load];
return [self.records retrievePayStubs];
}
Now, we only load the employee records when we actually need them. If they've already been loaded, we don't do any extra work (aside from one method call). If we never need the payment records, then we don't need to do the work at all.
The class only works when it has to -- and waits 'til the last minute to do the work. It's "lazy!"
Here's a pattern base on a couple of DataImporter & a DataManager the first initializing is data property only when the second request the property
#interface DataImporter: NSObject
#property (readonly) NSString* data;
#end
#implementation DataImporter
#synthesize data;
-(id)init {
if (!(self = [super init])) { return nil; }
_NSLog(#"%#", #"Importing...");
data = #"DATA";
_NSLog(#"%#", #"Importation completed!");
return self;
}
#end
#interface DataManager: NSObject {
#private
DataImporter* _importer;
}
-(NSString*)loadData;
#end
#implementation DataManager
-(id)init {
if (!(self = [super init])) { return nil; }
_NSLog(#"DataManager initialized%#",#"!");
return self;
}
-(NSString*)loadData {
if (!_importer) {
_importer = [DataImporter new];
}
return _importer.data;
}
#end
DataManager* dm = [DataManager new]; //>> DataManager initialized
NSString* data = [dm loadData]; //>> Importing
//>> Importation completed!
_NSLog(#"%#", data); //>> DATA)
I am trying to follow through examples from other as well as Apple. I'm lost.
I have a singleton class that I use to handle my user logging in (challenges a web server, etc.).
I want to create a block I can call, pass in the username/password. The block will perform the web service calls then return if it was successful or not.
This is what i've manage to get working so far:
My singleton class looks like this:
.h
typedef void (^AMLoginBlock)(NSString *userName, NSString *password);
#interface AuthManager : NSObject
+ (id)sharedManager;
+ (bool)loginUserWithBlock:(AMLoginBlock)block;
#end
.m
#implementation AuthManager
+ (id)sharedManager
{
static dispatch_once_t pred = 0;
__strong static id _sharedObject = nil;
dispatch_once(&pred, ^{
_sharedObject = [[self alloc] init]; // or some other init method
});
return _sharedObject;
}
+ (bool)loginUserWithBlock:(AMLoginBlock)block {
NSLog(#"im printing from AM");
return true;
}
#end
I then call the method like so:
bool rtn = [AuthManager loginUserWithBlock:^(NSString *userName, NSString *password) {
NSLog(#"im here in the block LVC.");
}];
My question is three parts:
How do I write a completion handler for the block similar to UIView animation... block.
Is it a good idea to perform these web service calls from a block based implementation?
Should I be declaring the block method like so:
- (bool)loginUserWithBlock:(AMLoginBlock)block;
instead of using +(bool)loginUser.. since it is in a singleton class. Not sure if this will cause multiple instances of the singleton to be created.
My goal is to be able to call this block like you call [UIView animation..]. So I can simply do:
[AuthManager loginUserWithUsername:foo
password:bar1
completion:^(BOOL finished) {
if (finished)
//push new view controller.
else
//spit out error
}];
Completion Handler
You will want to copy the completion block to a class iVar:
#property (nonatomic, copy) void (^completionHandler)(bool *);
Because you are saving the block, you need to have a non-class method take the block (see following for how to do this without violating your singleton). An example of your method could be:
- (void)loginUserWithUsername:(NSString *)username
password:(NSString *)password
completion:(void(^)(bool *finished))completionBlock
{
// Copy the blocks to use later
self.completionHandler = completionBlock;
// Run code
[self doOtherThings];
}
Then when your login code has finished its work, you can call the block - here I pass self.error, a bool to the block :
- (void)finishThingsUp
{
// We are done with all that hard work. Lets call the block!
self.completionHandler(self.error);
// Clean up the blocks
self.completionHandler = nil;
}
Good Idea
Well, this is a philosophical question, but I will say this: Blocks in Objective-C allow you to write code that performs a single task and easily integrate it into many programs. If you chose to not use a completion handler in your login code you would need your login code to:
Require that classes using it implement a protocol (as in a LoginDelegate)
Use some other system of informing your code such as Key Value observing or Notifications
Hard code it to only work with one type of calling class
Any of the above approaches are fine, I feel a block-based call back system is the simplest and most flexible. It allows you to just use your class without worrying about additional infrastructure (setting up notifications, conforming to protocols, etc. ) while still letting you reuse it in other classes or programs.
Singelton
Methods that begin with a + in Objective-C are class methods. You cannot use class methods to manipulate iVars, as who would own that data?
What you can do is have a class method that always returns the same instance of that class, allowing you to have an object that can own data, but avoid ever having more than one of them.
This excellent Stack Overflow answer has sample code.
Good Luck!
I have two classes that can act as a delegate of a third class, and both implement a formal protocol made entirely of optional methods. One of the classes implements everything while another only implements a couple methods that i care about. However, at runtime when i have the second class act as the delegate to the third class, and the third class ends up calling one of the unimplemented optional methods on that delegate, i get a runtime error essentially saying "Target does not respond to this message selector." I thought that objective-c handled this case correctly, and that it would just do nothing if that method wasn't actually defined on the class. Might there be something i'm missing?
When you call an optional method of your delegate, you need to make sure it responds to the selector before calling it:
if ([delegate respondsToSelector:#selector(optionalMethod)])
[delegate optionalMethod];
Optional protocol methods simply mean the object implementing the protocol does not have to implement the method in question - the callee then absolutely must check whether the object implements the method before calling (otherwise you'll crash, as you noticed). These NSObject HOM categories can be helpful:
#implementation NSObject (Extensions)
- (id)performSelectorIfResponds:(SEL)aSelector
{
if ( [self respondsToSelector:aSelector] ) {
return [self performSelector:aSelector];
}
return NULL;
}
- (id)performSelectorIfResponds:(SEL)aSelector withObject:(id)anObject
{
if ( [self respondsToSelector:aSelector] ) {
return [self performSelector:aSelector withObject:anObject];
}
return NULL;
}
#end
Then you can simply do:
[delegate performSelectorIfResponds:#selector(optionalMethod)];
This Blocks solution works well, once you get your head wrapped around what is going on. I added a BOOL result because I wanted to be able to conditionally run one of several optional methods. Some tips if you are trying to implement this solution:
First, if you haven't encountered Extension/Categories yet, you simply add this to the top of your class, OUTSIDE the existing class definition. It will be a public or private extension based on where you put it.
#implementation NSObject (Extensions)
// add block-based execution of optional protocol messages
-(BOOL) performBlock:(void (^)(void))block ifRespondsTo:(SEL) aSelector
{
if ([self respondsToSelector:aSelector]) {
block();
return YES;
}
return NO;
}
#end
Second, here's how you call it from your code:
BOOL b = [(NSObject*)self.delegate performBlock:^{
// code to run if the protocol method is implemented
}
ifRespondsTo:#selector(Param1:Param2:ParamN:)];
Replace Param1:Param2:ParamN: with the names of each parameter for your protocol method. Each one should end with a colon.
So if your protocol method looks like:
-(void)dosomething:(id)blah withObj:(id)blah2 andWithObj(id)blah3;
the last line would look like this:
ifRespondsTo:#selector(dosomething:withObj:andWithObj:)];
Blocks might provide a better solution. They allow to conditionally perform any code based on the existence of an implementation of a given method:
-(void) performBlock:(void (^)(void))block ifRespondsTo:(SEL) aSelector{
if ([self respondsToSelector:aSelector]) {
block();
}
}
By using this addition to NSObject, you can conditionally execute any #optional method, no matter how many parameters it might have.
See How to safely send #optional protocol messages that might not be implemented
I need to hide (make private) the -init method of my class in Objective-C.
How can I do that?
NS_UNAVAILABLE
- (instancetype)init NS_UNAVAILABLE;
This is a the short version of the unavailable attribute. It first appeared in macOS 10.7 and iOS 5. It is defined in NSObjCRuntime.h as #define NS_UNAVAILABLE UNAVAILABLE_ATTRIBUTE.
There is a version that disables the method only for Swift clients, not for ObjC code:
- (instancetype)init NS_SWIFT_UNAVAILABLE;
unavailable
Add the unavailable attribute to the header to generate a compiler error on any call to init.
-(instancetype) init __attribute__((unavailable("init not available")));
If you don't have a reason, just type __attribute__((unavailable)), or even __unavailable:
-(instancetype) __unavailable init;
doesNotRecognizeSelector:
Use doesNotRecognizeSelector: to raise a NSInvalidArgumentException. “The runtime system invokes this method whenever an object receives an aSelector message it can’t respond to or forward.”
- (instancetype) init {
[self release];
[super doesNotRecognizeSelector:_cmd];
return nil;
}
NSAssert
Use NSAssert to throw NSInternalInconsistencyException and show a message:
- (instancetype) init {
[self release];
NSAssert(false,#"unavailable, use initWithBlah: instead");
return nil;
}
raise:format:
Use raise:format: to throw your own exception:
- (instancetype) init {
[self release];
[NSException raise:NSGenericException
format:#"Disabled. Use +[[%# alloc] %#] instead",
NSStringFromClass([self class]),
NSStringFromSelector(#selector(initWithStateDictionary:))];
return nil;
}
[self release] is needed because the object was already allocated. When using ARC the compiler will call it for you. In any case, not something to worry when you are about to intentionally stop execution.
objc_designated_initializer
In case you intend to disable init to force the use of a designated initializer, there is an attribute for that:
-(instancetype)myOwnInit NS_DESIGNATED_INITIALIZER;
This generates a warning unless any other initializer method calls myOwnInit internally. Details will be published in Adopting Modern Objective-C after next Xcode release (I guess).
Apple has started using the following in their header files to disable the init constructor:
- (instancetype)init NS_UNAVAILABLE;
This correctly displays as a compiler error in Xcode. Specifically, this is set in several of their HealthKit header files (HKUnit is one of them).
Objective-C, like Smalltalk, has no concept of "private" versus "public" methods. Any message can be sent to any object at any time.
What you can do is throw an NSInternalInconsistencyException if your -init method is invoked:
- (id)init {
[self release];
#throw [NSException exceptionWithName:NSInternalInconsistencyException
reason:#"-init is not a valid initializer for the class Foo"
userInfo:nil];
return nil;
}
The other alternative — which is probably far better in practice — is to make -init do something sensible for your class if at all possible.
If you're trying to do this because you're trying to "ensure" a singleton object is used, don't bother. Specifically, don't bother with the "override +allocWithZone:, -init, -retain, -release" method of creating singletons. It's virtually always unnecessary and is just adding complication for no real significant advantage.
Instead, just write your code such that your +sharedWhatever method is how you access a singleton, and document that as the way to get the singleton instance in your header. That should be all you need in the vast majority of cases.
You can declare any method to be not available using NS_UNAVAILABLE.
So you can put these lines below your #interface
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
Even better define a macro in your prefix header
#define NO_INIT \
- (instancetype)init NS_UNAVAILABLE; \
+ (instancetype)new NS_UNAVAILABLE;
and
#interface YourClass : NSObject
NO_INIT
// Your properties and messages
#end
That depends on what you mean by "make private". In Objective-C, calling a method on an object might better be described as sending a message to that object. There's nothing in the language that prohibits a client from calling any given method on an object; the best you can do is not declare the method in the header file. If a client nevertheless calls the "private" method with the right signature, it will still execute at runtime.
That said, the most common way to create a private method in Objective-C is to create a Category in the implementation file, and declare all of the "hidden" methods in there. Remember that this won't truly prevent calls to init from running, but the compiler will spit out warnings if anyone tries to do this.
MyClass.m
#interface MyClass (PrivateMethods)
- (NSString*) init;
#end
#implementation MyClass
- (NSString*) init
{
// code...
}
#end
There's a decent thread on MacRumors.com about this topic.
If you are talking about the default -init method then you can't. It's inherited from NSObject and every class will respond to it with no warnings.
You could create a new method, say -initMyClass, and put it in a private category like Matt suggests. Then define the default -init method to either raise an exception if it's called or (better) call your private -initMyClass with some default values.
One of the main reasons people seem to want to hide init is for singleton objects. If that's the case then you don't need to hide -init, just return the singleton object instead (or create it if it doesn't exist yet).
Put this in header file
- (id)init UNAVAILABLE_ATTRIBUTE;
well the problem why you can't make it "private/invisible" is cause the init method gets send to id (as alloc returns an id) not to YourClass
Note that from the point of the compiler (checker) an id could potencialy respond to anything ever typed (it can't check what really goes into the id at runtime), so you could hide init only when nothing nowhere would (publicly = in header) use a method init, than the compile would know, that there is no way for id to respond to init, since there is no init anywhere (in your source, all libs etc...)
so you cannot forbid the user to pass init and get smashed by the compiler... but what you can do, is to prevent the user from getting a real instance by calling a init
simply by implementing init, which returns nil and have an (private / invisible) initializer which name somebody else won't get (like initOnce, initWithSpecial ...)
static SomeClass * SInstance = nil;
- (id)init
{
// possibly throw smth. here
return nil;
}
- (id)initOnce
{
self = [super init];
if (self) {
return self;
}
return nil;
}
+ (SomeClass *) shared
{
if (nil == SInstance) {
SInstance = [[SomeClass alloc] initOnce];
}
return SInstance;
}
Note : that somebody could do this
SomeClass * c = [[SomeClass alloc] initOnce];
and it would in fact return a new instance, but if the initOnce would nowhere in our project be publicly (in header) declared, it would generate a warning (id might not respond ...) and anyway the person using this, would need to know exactly that the real initializer is the initOnce
we could prevent this even further, but there is no need
I have to mention that placing assertions and raising exceptions to hide methods in the subclass has a nasty trap for the well-intended.
I would recommend using __unavailable as Jano explained for his first example.
Methods can be overridden in subclasses. This means that if a method in the superclass uses a method that just raises an exception in the subclass, it probably won't work as intended. In other words, you've just broken what used to work. This is true with initialization methods as well. Here is an example of such rather common implementation:
- (SuperClass *)initWithParameters:(Type1 *)arg1 optional:(Type2 *)arg2
{
...bla bla...
return self;
}
- (SuperClass *)initWithLessParameters:(Type1 *)arg1
{
self = [self initWithParameters:arg1 optional:DEFAULT_ARG2];
return self;
}
Imagine what happens to -initWithLessParameters, if I do this in the subclass:
- (SubClass *)initWithParameters:(Type1 *)arg1 optional:(Type2 *)arg2
{
[self release];
[super doesNotRecognizeSelector:_cmd];
return nil;
}
This implies that you should tend to use private (hidden) methods, especially in initialization methods, unless you plan to have the methods overridden. But, this is another topic, since you don't always have full control in the implementation of the superclass. (This makes me question the use of __attribute((objc_designated_initializer)) as bad practice, although I haven't used it in depth.)
It also implies that you can use assertions and exceptions in methods that must be overridden in subclasses. (The "abstract" methods as in Creating an abstract class in Objective-C )
And, don't forget about the +new class method.