There are 3 behaviors in my method, I'm pretty sure animationOptions is the one causing bug. AnimationOptions is only used to ban rotation.If i delete this behavior,my code work fine.
Here is my bug.Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil’
After I add a exception breakpoint, the breakpoint stay at this line:
[self addChildBehavior:self.animationOptions];
If i delete this line, my code work fine.
But how can i fix this bug, I can not find where is this line mistake
Here is my DropitBehavior.m
#import "DropitBehavior.h"
#interface DropitBehavior()
#property(strong,nonatomic) UIGravityBehavior *gravity;
#property(strong,nonatomic) UICollisionBehavior *collider;
#property(strong,nonatomic) UIDynamicItemBehavior *animationOptions;
#end
#implementation DropitBehavior
-(UIGravityBehavior *)gravity
{
if (!_gravity) {
_gravity=[[UIGravityBehavior alloc]init];
_gravity.magnitude=0.90;
}
return _gravity;
}
-(UICollisionBehavior *)collider
{
if (!_collider) {
_collider=[[UICollisionBehavior alloc]init];
_collider.translatesReferenceBoundsIntoBoundary=YES;
}
return _collider;
}
-(UIDynamicItemBehavior *)animationOptions
{
if (_animationOptions) {
_animationOptions=[[UIDynamicItemBehavior alloc]init];
_animationOptions.allowsRotation=NO;
}
return _animationOptions;
}
-(void)additem:(id <UIDynamicItem>)item
{
[self.gravity addItem:item];
[self.collider addItem:item];
[self.animationOptions addItem:item];
}
-(void)removeitem:(id <UIDynamicItem>)item
{
[self.gravity removeItem:item];
[self.collider removeItem:item];
[self.animationOptions removeItem:item];
}
-(instancetype)init
{
self=[super init];
[self addChildBehavior:self.gravity];
[self addChildBehavior:self.collider];
[self addChildBehavior:self.animationOptions];
return self;
}
#end
a NSMutable array doesn't accept a "nil" to be added to itself
"self.animationOptions" will call the
-(UIDynamicItemBehavior *)animationOptions
method and the method will return only "nil" all the time.
-(UIDynamicItemBehavior *)animationOptions
{
if (_animationOptions) {
_animationOptions=[[UIDynamicItemBehavior alloc]init];
your logic in this piece of code doesn't allow the system to construct "_animationOptions" object.
if (!_animationOptions)
{
..code..
}
would help
Related
I have a UIViewController subclass (say MyViewController).
MyViewController.h
#protocol TargetChangedDelegate
-(void) targetChanged;
#end
#interface MyViewController
#property (weak) id<TargetChangedDelegate> targetChangedDelegate;
-(void) doSomethingOnYourOwn;
#end
MyViewController.m
#implementation MyViewController <TargetChangedDelegate>
-(void) doSomethingOnYourOwn
{
// DO some stuff here
// IS THIS BAD ??
self.targetChangedDelegate = self;
}
-(IBAction) targetSelectionChanged
{
[self.targetChangedDelegate targetChanged];
}
-(void) targetChanged
{
// Do some stuff here
}
#end
Based on certain conditions a class that instantiates an instance of MyViewController may decide to set itself as the delegate or not.
Foo.m
#property(strong) MyViewController *myVC;
-(void) configureViews
{
self.myVC = [[MyViewController alloc] init];
[self.view addSubview:self.myVC];
if (someCondition)
{
self.myVC.targetChangedDelegate = self;
}
else
{
[self.myVC doSomethingOnYourOwn]
//MyViewController sets itself as the targetChangedDelegate
}
}
With reference to the code snippet above, I have the following question:
Is it a violation of MVC/delegation design pattern (or just a bad design) to say:
self.delegate = self;
There's absolutely no problem with setting the delegate to self. In fact it is a good way to provide default delegate functionality if a delegate is not set by somebody else.
Obviously, the delegate property has to be declared weak otherwise you get a reference cycle.
To expand a bit, having read the wrong answer and wrong comments above, if you allow an object to be its own delegate, your code is cleaner because you do not have to surround absolutely every single delegate call with
if ([self delegate] != nil)
{
[[self delegate] someMethod];
}
else
{
[self someMethod];
}
Its not proper way to assign self.delegate = self.
for your functionality, you can do this:
-(void) doSomethingOnYourOwn
{
// DO some stuff here
self.targetChangedDelegate = nil;
}
and when using delegate:
if(self.targetChangedDelegate != nil && [self.targetChangedDelegate respondsToSelector:#selector(targetChanged)]
{
[self.targetChangedDelegate targetChanged];
}
else
{
[self targetChanged];
}
It is bad design to set self.delegate = self; it should be another object. Delegation via protocols are an alternative design to subclassing and you can read more about delegation here:
https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/Delegation.html
And here is more on protocols:
https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/Protocol.html
I am working on an Analytics Project by Swizzling UIViewController methods viewDidAppear and viewDidAppear, code snippet as follows,
- (void) swizzledViewDidAppear : (BOOL)animated {
if ([UA isAppInitialized]) { // Check if Analytics Initialized
if ([[self class] isSubclassOfClass:[UIViewController class]]) {
[UA startPage:[NSString stringWithFormat:#"%#", NSStringFromClass ([self class])]];
}
}
[self swizzledViewDidAppear:animated];
}
- (void) swizzledViewDidDisappear : (BOOL)animated {
if ([UA isAppInitialized]) { // Check if Analytics Initialized
if ([[self class] isSubclassOfClass:[UIViewController class]]) {
[UA endPage:[NSString stringWithFormat:#"%#", NSStringFromClass ([self class])]];
}
}
[self swizzledViewDidDisappear:animated];
}
This is the code snippet where I want to track only the Custom ViewController, ex: MyViewController or FooViewController...etc and not Framework related classes like UICompatibilityInputViewController, UIInputWindowController...etc.
Please let me know how can I achieve this. I tried to check for Subclass but still at one point Framework classes are getting recorded.
Thanks,
Vijay
you can get a class's NSBundle and check if that bundle is yours.
swift example using an extension:
//code
extension NSObject {
static var isOurClass: Bool {
let appBundle = Bundle.main
let clsBundle = Bundle(for: self.self);
return clsBundle.bundlePath.hasPrefix(appBundle.bundlePath)
}
}
//test
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
print("appdelegate class ours? \(AppDelegate.isOurClass)")
print("NSString class ours? \(NSString.isOurClass)")
return true
}
}
basic objc example:
#import <Foundation/Foundation.h>
#interface T : NSObject
#end
#implementation T
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
NSBundle *bundleOfApp = [NSBundle mainBundle];
NSLog(#"%#", bundleOfApp.bundlePath);
//ours
T *t = [T new];
NSBundle *bundleOfT = [NSBundle bundleForClass:t.class];
NSLog(#"%#", bundleOfT.bundlePath);
if([bundleOfT.bundlePath hasPrefix:bundleOfApp.bundlePath]) {
NSLog(#"ours");
}
//theirs
bundleOfT = [NSBundle bundleForClass:NSString.class];
NSLog(#"%#", bundleOfT.bundlePath);
if([bundleOfT.bundlePath hasPrefix:bundleOfApp.bundlePath]) {
}
else {
NSLog(#"theirs");
}
}
}
In this case you have make instance of your class like and check it like
MyViewController *myVcntrl = [[MyViewController alloc]init];
if ([myVcntrl isKindOfClass:[MyViewController class]]) {
NSLog(#"Class is of MyViewController type");
}
else {
NSLog(#"Not of MyViewController type");
}
I was swizzling viewDidLoad, loadView and awakeFromNib for measuring load times and there is some strange behavior I've noticed.
For framework classes like UIInputWindowController, UIKeyboardCandidateGridCollectionViewController : awakeFromNib or loadView doesn't get called. They immediately appear after viewDidLoad has been called.
So the following lines of codes:
- (void)swizzled_awakeFromNib
{
NSLog(#"*** Awake from nib %#", NSStringFromClass(self.class));
[self swizzled_awakeFromNib];
}
- (void)swizzled_viewDidLoad
{
NSLog(#"*** View did load %#", NSStringFromClass(self.class));
[self swizzled_viewDidLoad];
}
- (void)swizzled_loadView
{
NSLog(#"*** Load view %#", NSStringFromClass(self.class));
[self swizzled_loadView];
}
Prints:
*** Awake from nib UINavigationController
*** View did load UINavigationController
*** Load view MyLoginViewController
*** View did load UIInputWindowController
*** View did load UIKeyboardCandidateGridCollectionViewController
*** View did load UIInputWindowController
*** View did load UIApplicationRotationFollowingControllerNoTouches
*** View did load MyLoginViewController
So maybe you can add a property to your category and set it to YES if awakeFromNib and/or loadView has called. Then check that bool in your viewDidLoad method to see if its a framework class.
I just build a class to manage correctly my database and JSON request. The problem is that now, how can I perform the segue ?
Here is my code
In my view :
- (IBAction)loginClick:(id)sender
{
NSString *post = [NSString stringWithFormat:#"username=test&password=test"];
[[DataManagement sharedManager] WebServiceLogin:post];
}
- (void) showTypeView
{
[self performSegueWithIdentifier:#"showTypeView" sender:nil];
}
In my class :
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
...
switch ([[response valueForKey:#"success"] intValue])
{
case 0:
{
NSLog(#"error: %# error Description: %#", [response valueForKey:#"success"], [response valueForKey:#"error_message"]);
break;
}
case 1:
{
LoginViewController *showView = [LoginViewController new];
[showView showTypeView];
break;
}
default:
break;
}
...
}
When I launch, I have an error :
**
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Receiver (<LoginViewController: 0x165afd30>) has no segue with identifier 'showTypeView''
*** First throw call stack:
(0x2592e2eb 0x250fadff 0x29e2b037 0xe1819 0xdb64f 0x25f64de1 0x25f64d99 0x25f64e8d 0x25e261ef 0x25edf04f 0xa77cab 0xa7f835 0x25e171e3 0x258415f9 0x25e170cb 0x25e16f95 0x25e16e29 0x258f1257 0x258f0e47 0x258ef1af 0x25841bb9 0x258419ad 0x26abbaf9 0x29b2dfb5 0xe3ea9 0x254f4873)
libc++abi.dylib: terminating with uncaught exception of type NSException
**
If you're using segueWithIdentifier then you need to already have the segue built in Storyboard and labeled correctly as "showTypeView". Otherwise you should use a navigation controller to push a view controller or use self presentViewController to show a modal view controller.
EDIT:
Building off of Larme's comment, you can build a delegate like this:
// In your class.h file
#property (weak, nonatomic)id<SegueDelegate> delegate;
// In class.m file
LoginViewController *showView = [LoginViewController new];
self.delegate = showView;
[self.delegate segue];
// In LoginViewController.h
#protocol SegueDelegate
-(void)segue;
#end
#interface LoginViewController: UIViewController <SegueDelegate>
-(void)segue;
#end
// In LoginViewController.m
#implementation LoginViewController
-(void)segue
{
[self performSegueWithIdentifier:#"showTypeView" sender:nil];
}
#end
I noticed the following behavior when using OCMock, and after poking around I discovered it is an issue with how the Xcode debugger handles methods that are not implemented. Here's an example:
#implementation ForwardingClass
- (id)init {
self = [super init];
self.delegate = [[DelegateClass alloc] init];
return self;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
[anInvocation invokeWithTarget:self.delegate];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
return [self.delegate methodSignatureForSelector:sel];
}
- (BOOL)respondsToSelector:(SEL)aSelector {
return YES;
}
#end
#implementation DelegateClass
- (void)forwardedMethod {
NSLog(#"got it!");
}
#end
Now we can call it:
ForwardingClass *forwardingClass = [[ForwardingClass alloc] init];
// Xcode will not step into this method
[(id)forwardingClass forwardedMethod];
Xcode will not step into the forwardedMethod. Even worse, if I set a breakpoint in forwardInvocation it won't break there. The net effect is that you can't step into any object that has been partially mocked by OCMock.
Any ideas how to fix this?
Is it a bug (or unintended "feature") in Xcode?
I've tried this on both Xcode 6 and Xcode 7 beta 4.
I am trying to make a calculator app, but when I press enter nothing is pushed into the array. I have a class called CaculatorBrain where the pushElement method is defined, however (for now) I defined and implemented pushElement method in the view controller.
When I log the operand object as it is typed in the console when enter is pressed the contents of array is nil! Why is that?
#import "CalculatorViewController.h"
#import "CalculatorBrain.h"
#interface CalculatorViewController ()
#property (nonatomic)BOOL userIntheMiddleOfEnteringText;
#property(nonatomic,copy) NSMutableArray* operandStack;
#end
#implementation CalculatorViewController
BOOL userIntheMiddleOfEnteringText;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
-(NSMutableArray*) operandStack {
if (_operandStack==nil) {
_operandStack=[[NSMutableArray alloc]init];
}
return _operandStack;
}
-(CalculatorBrain*)Brain
{
if (!_Brain) _Brain= [[CalculatorBrain alloc]init];
return _Brain;
}
- (IBAction)digitPressed:(UIButton*)sender {
if (self.userIntheMiddleOfEnteringText) {
NSString *digit= [sender currentTitle];
NSString *currentDisplayText=self.display.text;
NSString *newDisplayText= [currentDisplayText stringByAppendingString:digit];
self.display.text=newDisplayText;
NSLog(#"IAm in digitPressed method");
}
else
{
NSString *digit=[sender currentTitle];
self.display.text = digit;
self. userIntheMiddleOfEnteringText=YES;
}
}
-(void)pushElement:(double)operand {
NSNumber *operandObject=[NSNumber numberWithDouble:operand];
[_operandStack addObject:operandObject];
NSLog(#"operandObject is %#",operandObject);
NSLog(#"array contents is %#",_operandStack);
}
- (IBAction)enterPressed {
[self pushElement: [self.display.text doubleValue] ];
NSLog(#"the contents of array is %#",_operandStack);
userIntheMiddleOfEnteringText= NO;
}
It looks like the operand stack is never initialized.
When you directly access _operandStack you don't go through -(NSMutableArray*) operandStack, which is the only place where the operand stack is allocated and initialized. If the array isn't allocated you can't put anything in it, which is why it logs the contents as nil.
I'd recommend using either self.operandStack (which uses the method that checks if _operandStack is nil) everywhere except inside the -(NSMutableArray*) operandStack method, or allocating the operand stack in your viewDidLoad.