Subclass WKWebview crash on dealloc in iOS - ios

I'm writing an app using WKWebView framework. I declare a new class named CustomWebView inherited from WKWebView
#interface CustomWebView : WKWebView {
id customObject;
}
#end
#implement CustomWebView
- (id)init {
if (self = [super init]) {
customObject = [[NSObject alloc] init];
}
return self;
}
- (void)dealloc {
//Doing my dealloc stuff
[customObject release];
[super dealloc];
}
#end
I declare a new instance of CustomWebView on another UIView class
cusWebview = [[CustomWebView alloc] init];
cusWebview.backgroundColor = [UIColor clearColor];
cusWebview.layer.cornerRadius = 4;
cusWebview.UIDelegate = self;
cusWebview.navigationDelegate = self;
[self addSubView:cusWebview];
[cusWebview release];
But when CustomWebView instance dealloc, it crashes on its dealloc method on line
[super dealloc];
just break on this line with EXC_BAD_ACCESS
Can anyone know the reason

You don't need to be calling [super dealloc]
Also make sure you aren't adding observing any KVO's and not removing them in dealloc.
To be safe, I would also set your navigationDelegate and UIDelegate to nil on dealloc.

Related

Pass NSArray to UIView

I presumed this would be simple but it is not working.
I am trying to pass an NSArray to my UIView that is getting imported with a NIB.I am importing it as:
DraggableViewBackground *draggableBackground = [[DraggableViewBackground alloc]initWithFrame:frame];
draggableBackground.exampleCardLabels = #[#"Mercedes-Benz", #"BMW", #"Porsche",
#"Opel", #"Volkswagen", #"Audi"];
[self.ripContainer addSubview:draggableBackground];
On my DraggableViewBackground UIView
.h
#property (retain,nonatomic)NSArray* exampleCardLabels;
.m
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[super layoutSubviews];
NSLog(#"Dish RIP %#", exampleCardLabels);
}
return self;
}
I am currently getting a null value. I am aware this is 101 basics when passing data but I don't understand why this isn't working.
It's just timing.
At this stage the array has not been set:
DraggableViewBackground *draggableBackground = [[DraggableViewBackground alloc]initWithFrame:frame];
and yet you are logging the array in the initWithFrame method. Log it later on in the lifecycle.
Also don't call [super layoutSubviews]; in the initWithFrame method.
Make your init like this:
- (id)initWithFrame:(CGRect)frame andCards:(NSArray *)cards{
self = [super initWithFrame:frame];
if (self){
self.xampleCardLabels = cards;
}
return self;
}
and call it
DraggableViewBackground *draggableBackground = [[DraggableViewBackground alloc]
initWithFrame: frame
andCards: #[#"Mercedes-Benz", #"BMW", #"Porsche",#"Opel", #"Volkswagen", #"Audi"]];

Passing variable From UIViewController to UIView

I have tried modify my code many times, but still can't pass variable from UIViewController to UIView, variable always return (null).
// BPGraphView.h
#interface BPGraphView : UIView
#property (nonatomic, retain) NSString *test;
#end
// BPGraphView.m
#import "BPGraphView.h"
#implementation BPGraphView
#synthesize test;
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
NSLog(#"test %#",test);
if (self) {
// Initialization code
}
return self;
}
- (void)drawRect:(CGRect)rect
{
NSLog(#"draw %#", test); // always return (null)
if ([test isEqual:#"something"]) {
[self drawOutLine];
}
}
#end
// BloodPressureViewController.m
- (void)viewDidLoad
{
BPGraphView * graphview=[[BPGraphView alloc] init];
graphview.test = #"something";
}
In your code:
- (void)viewDidLoad
{
BPGraphView * graphview=[[BPGraphView alloc] init];
graphview.test = #"dd";
}
The variable graphview is basically destroyed once the viewDidLoad is run to finish. It will never get a chance to run drawRect.
Now the question is how should you define a instance variable of BPGraphView in your UIViewController. The easiest way should be adding a BPGraphView onto your view's xib file and link to an IBOutlet in your UIViewController. In this way, you should be able to assign to test
#IBOutlet BPGraphView graphview;
- (void)viewDidLoad
{
graphview.test = #"dd";
}
Without setting a frame, drawRect will not be called.
BPGraphView * graphview=[[BPGraphView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
graphview.test = #"something";
[self.view addSubview:graphview];
I tried this,it works well,in your code,drawRect is not called
BPGraphView * graphview=[[BPGraphView alloc] init];
graphview.test = #"dd";
[graphview drawRect:CGRectMake(0, 0, 0, 0)];

Add category to UITextView and Override it's init function while UITextView is in xib

I can't intercept the init function that's getting called when it's getting created inside of the xib file.
I want to add borderline to it when it gets created so that I won't need to add it manually.
.h:
#import <UIKit/UIKit.h>
#interface UITextView (FITAddBorderline)
- (id) init;
- (id) initWithFrame:(CGRect)frame;
#end
.m:
#import "UITextView+FITAddBorderline.h"
#implementation UITextView (FITAddBorderline)
- (id) init
{
self = [super init];
if (self) {
[self addBorderline];
}
return self;
}
- (id) initWithFrame:(CGRect)frame {
self = [super init];
if (self) {
self.frame = frame;
[self addBorderline];
}
return self;
}
- (void) addBorderline {
//To make the border look very close to a UITextField
[self.layer setBorderColor:[[[UIColor grayColor] colorWithAlphaComponent:0.5] CGColor]];
[self.layer setBorderWidth:2.0];
//The rounded corner part, where you specify your view's corner radius:
self.layer.cornerRadius = 5;
self.clipsToBounds = YES;
}
#end
Views that come from NIBs are initialized with initWithCoder:
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[self addBorderline];
}
return self;
}
As a side note, I would recommend changing what you are doing and use a subclass instead of a category. You can get yourself into some trouble overriding methods in a category. See more info here.
You just need to implement the awakeFromNib method:
-(void)awakeFromNib
{
[super awakeFromNib];
[self addBorderline];
}

iOS: KVO Memory Leak?

Note: I am posting this as a reference for other developers that might run into the same issue.
Why do I have a memory leak with this code:
#interface SPWKThing : NSObject
#property (strong, nonatomic) NSArray *things;
#end
#implementation SPWKThing {
BOOL _isKVORegistered;
}
- (id)init
{
self = [super init];
if (self) {
NSLog(#"initing SPWKThing");
[self registerKVO];
}
return self;
}
- (void)didChangeValueForKey:(NSString *)key {
if ([key isEqualToString:#"things"]) {
NSLog(#"didChangeValueForKey: things have changed!");
}
}
#pragma mark - KVO
- (void)registerKVO
{
if (!_isKVORegistered) {
NSLog(#"Registering KVO, and things is %#", _things);
[self addObserver:self forKeyPath:#"things" options:0 context:NULL];
_isKVORegistered = YES;
}
}
- (void)unregisterKVO
{
if (_isKVORegistered) {
NSLog(#"Unregistering KVO");
[self removeObserver:self forKeyPath:#"things"];
_isKVORegistered = NO;
}
}
- (void)dealloc
{
NSLog(#"SPWKThing dealloc");
[self unregisterKVO];
}
#end
#implementation SPWKViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self runDemo];
}
- (void)runDemo
{
SPWKThing *thing = [[SPWKThing alloc] init];
thing.things = #[#"one", #"two", #"three"];
thing = nil;
}
#end
My output is:
initing SPWKThing
Registering KVO, and things is (null)
didChangeValueForKey: things have changed!
dealloc is never called? Why? I am setting thing = nil in the last line of runDemo!
See a demo project here: https://github.com/jfahrenkrug/KVOMemoryLeak
The answer is:
Never override didChangeValueForKey: (at least not without calling super). The documentation does not warn you about this.
Use the correct method observeValueForKeyPath:ofObject:change:context: instead.
This project clearly demonstrates this: https://github.com/jfahrenkrug/KVOMemoryLeak

Why do both init functions get called

If I have a class that is setup like this to customize a UIView.
#interface myView : UIView
- (id)init {
self = [super init];
if(self){
[self foo];
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self foo];
}
return self;
}
-(void) foo{
//Build UIView here
}
How come foo is called twice whether I use
myView *aView = [[myView alloc]init]];
or
myView *aView = [[myView alloc]initWithFram:aFrame];
UIView init calls UIView initWithFrame:. Since you override both, calling your init method results in your initWithFrame: method being called:
In other words: your init calls UIView init. UIView init calls initWithFrame:. Since you override initWithFrame:, your initWithFrame: is called which in turn calls UIView initWithFrame:.
The solution, since your initWithFrame: will always be called, is to remove the call to foo from your init method.

Resources