My English is not good, I try to describe the problem clearly.
I know that #property and #synthesize are just to get getter and setter methods. So we can use property by self.x and _x.
And self.x is just to call setter and getter methods.
The result of #property(strong) is get methods as following:
All is in ARC:
#property (nonatomic, strong) NSString *name;
- (NSString *)name {
return _name;
}
- (void)setName:(NSString *)name {
if (_name != name) {
_name = name;
}
}
So, my question is that if the use of #property(strong) is just to get methods, we can use the following.
- (void)setName:(NSString *)name {
if (_name != name) {
__weak _name = name;
}
}
When we use self.name to set setName: method call, and we get a weak name, even we use strong before, it looks right. But there is a other examples.
#protocol TestDelegate <NSObject>
#end
#interface Test : UIView
#property (nonatomic, weak) id<TestDelegate> delegate;
- (instancetype)initWithDelegate:(id<TestDelegate>)delegate;
#end
- (instancetype)initWithDelegate:(id<TestDelegate>)delegate {
self = [super init];
if (self) {
_delegate = delegate;
}
return self;
}
Use in ViewController, all is dealloc, no recycle. Then we use
#property (nonatomic, strong) id<TestDelegate> delegate;
- (instancetype)initWithDelegate:(id<TestDelegate>)delegate {
self = [super init];
if (self) {
self.delegate = delegate;
}
return self;
}
- (void)setDelegate:(id<TestDelegate>)delegate {
__weak _delegate = delegate;
}
All is dealloc, no recycle too. Because we use self.delegate in init method, setDelegate: method call and we get weak delegate, even we use strong before. Then we use _delegate = delegate, it will recycle!!!
It is puzzled for me that we use weak and _delegate = delegate, it run well, but we use strong, _delegate = delegate and custom weak set method, it is recycle.
Thanks!
The delegates must be weak type of properties. This will avoid retain cycle because there can be endless circle of retaining two objects between themselves. With using ARC this is example of using delegate: #property (nonatomic, weak) id <MyObjectDelegate> delegate;
Related
Is there a standard pattern for implementing a mutable/immutable object class pair in Objective-C?
I currently have something like the following, which I wrote based off this link
Immutable Class:
#interface MyObject : NSObject <NSMutableCopying> {
NSString *_value;
}
#property (nonatomic, readonly, strong) NSString *value;
- (instancetype)initWithValue:(NSString *)value;
#end
#implementation MyObject
#synthesize value = _value;
- (instancetype)initWithValue:(NSString *)value {
self = [self init];
if (self) {
_value = value;
}
return self;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
return [[MyMutableObject allocWithZone:zone] initWithValue:self.value];
}
#end
Mutable Class:
#interface MyMutableObject : MyObject
#property (nonatomic, readwrite, strong) NSString *value;
#end
#implementation MyMutableObject
#dynamic value;
- (void)setValue:(NSString *)value {
_value = value;
}
#end
This works, but it exposes the iVar. Is there a better implementation that remedies this situation?
Your solution follows a very good pattern: the mutable class does not duplicate anything from its base, and exposes an additional functionality without storing any additional state.
This works, but it exposes the iVar.
Due to the fact that instance variables are #protected by default, the exposed _value is visible only to the classes inheriting MyObject. This is a good tradeoff, because it helps you avoid data duplication without publicly exposing the data member used for storing the state of the object.
Is there a better implementation that remedies this situation?
Declare the value property in a class extension. An extension is like a category without a name, but must be part of the class implementation. In your MyMutableObject.m file, do this:
#interface MyMutableObject ()
#property(nonatomic, readwrite, strong) value
#end
Now you've declared your property, but it's only visible inside your implementation.
The answer from dasblinkenlight is correct. The pattern provided in the question is fine. I provide an alternative that differs in two ways. First, at the expense of an unused iVar in the mutable class, the property is atomic. Second, as with many foundation classes, a copy of an immutable instance simply returns self.
MyObject.h:
#interface MyObject : NSObject <NSCopying, NSMutableCopying>
#property (atomic, readonly, copy) NSString *value;
- (instancetype)initWithValue:(NSString *)value NS_DESIGNATED_INITIALIZER;
#end
MyObject.m
#import "MyObject.h"
#import "MyMutableObject.h"
#implementation MyObject
- (instancetype)init {
return [self initWithValue:nil];
}
- (instancetype)initWithValue:(NSString *)value {
self = [super init];
if (self) {
_value = [value copy];
}
return self;
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
// Do not use the iVar here or anywhere else.
// This pattern requires always using self.value instead of _value (except in the initializer).
return [[MyMutableObject allocWithZone:zone] initWithValue:self.value];
}
#end
MyMutableObject.h:
#import "MyObject.h"
#interface MyMutableObject : MyObject
#property (atomic, copy) NSString *value;
#end
MyMutableObject.m:
#import "MyMutableObject.h"
#implementation MyMutableObject
#synthesize value = _value; // This is not the same iVar as in the superclass.
- (instancetype)initWithValue:(NSString *)value {
// Pass nil in order to not use the iVar in the parent.
// This is reasonably safe because this method has been declared with NS_DESIGNATED_INITIALIZER.
self = [super initWithValue:nil];
if (self) {
_value = [value copy];
}
return self;
}
- (id)copyWithZone:(NSZone *)zone {
// The mutable class really does need to copy, unlike super.
return [[MyObject allocWithZone:zone] initWithValue:self.value];
}
#end
A fragment of test code:
NSMutableString *string = [NSMutableString stringWithString:#"one"];
MyObject *object = [[MyObject alloc] initWithValue:string];
[string appendString:#" two"];
NSLog(#"object: %#", object.value);
MyObject *other = [object copy];
NSAssert(object == other, #"These should be identical.");
MyMutableObject *mutable1 = [object mutableCopy];
mutable1.value = string;
[string appendString:#" three"];
NSLog(#"object: %#", object.value);
NSLog(#"mutable: %#", mutable1.value);
Some debugging right after the last line above:
2017-12-15 21:51:20.800641-0500 MyApp[6855:2709614] object: one
2017-12-15 21:51:20.801423-0500 MyApp[6855:2709614] object: one
2017-12-15 21:51:20.801515-0500 MyApp[6855:2709614] mutable: one two
(lldb) po mutable1->_value
one two
(lldb) po ((MyObject *)mutable1)->_value
nil
As mentioned in the comments this requires discipline in the base class to use the getter instead of the iVar. Many would consider that a good thing, but that debate is off-topic here.
A minor difference you might notice is that I have used the copy attribute for the property. This could be made strong instead with very little change to the code.
I have four classes MainVC, ParentClient and ChildClient1, ChildClient2(which are subclasses of ParentClient). ParentClient has a delegate to MainVC such that in MainVC
- (void)viewDidLoad
{
[super viewDidLoad];
[ParentClient instance].mainViewDelegate = self;
}
And then the ParentClient looks like this
#interface BaseClient : NSObject
#property (assign) id<MainVCInteraction> mainViewDelegate;
+(instancetype) instance;
#end
Now I want to access mainViewDelegate from ChildClient1, ChildClient2 and it returns me nil while [ParentClient instance].mainViewDelegate returns the correct value
Here is what I did I removed the BaseClient Class so that ChildClient1, ChildClient2 were no longer subclasses of BaseClient. I defined a objective-c protocol file MainVCInteaction.h and made Client1, Client2 look like this:
#import "MainVCInteraction.h"
#interface ChildClient1 : NSObject
#property (assign) id<MainVCInteraction> mainViewDelegate;
+(instancetype) instance;
#end
#import "MainVCInteraction.h"
#interface ChildClient2 : NSObject
#property (assign) id<MainVCInteraction> mainViewDelegate;
+(instancetype) instance;
#end
And then MainVC implements this protocol, I assigned the delegate like this
- (void)viewDidLoad
{
[super viewDidLoad];
[ChildClient1 instance].mainViewDelegate = self;
[ChildClient2 instance].mainViewDelegate = self;
}
I have a protocol in one class:
#protocol DataStorageManager
- (void) saveFile;
#end
#interface DataManager : NSObject
{
id <DataStorageManager> delegate;
}
#property (nonatomic, assign) id<DataStorageManager> delegate;
//methods
#end
and its implementation:
#implementation DataManager
#synthesize delegate;
#end
and I have another class which is the adapter between the first and the third one:
#import "DataManager.h"
#import "DataPlistManager.h"
#interface DataAdapter : NSObject <DataStorageManager>
#property (nonatomic,strong) DataPlistManager *plistManager;
- (void) saveFile;
#end
and its implementation
#import "DataAdapter.h"
#implementation DataAdapter
-(id) initWithDataPlistManager:(DataPlistManager *) manager
{
self = [super init];
self.plistManager = manager;
return self;
}
- (void) saveFile
{
[self.plistManager savePlist];
}
#end
So when I in first method try to call my delegate method like this
[delegate saveFile];
Nothing happened. I don't understand what's wrong with the realization - it's a simple adapter pattern realization. So I need to use the delegate which will call the methods from the third class. Any help?
You are not setting the delegate property. You need to do this,
-(id) initWithDataPlistManager:(DataPlistManager *) manager
{
self = [super init];
self.plistManager = manager;
self.plistManager.delegate = self;
return self;
}
Also, in DataManager class remove the ivar declaration, just declaring property is sufficient, the ivar gets automatically created. Call the delegate method as below,
if([self.delegate respondsToSelector:#selector(saveFile)] {
[self.delegate saveFile];
}
Hope that helps!
In your case you forget to set your protocol delegate and also need to call protocol method
by self.delegate....
I just Give Basic Idea for how to Create Protocol
Also Read This Question
#DetailViewController.h
#import <UIKit/UIKit.h>
#protocol MasterDelegate <NSObject>
-(void) getButtonTitile:(NSString *)btnTitle;
#end
#interface DetailViewController : MasterViewController
#property (nonatomic, assign) id<MasterDelegate> customDelegate;
#DetailViewController.m
if([self.customDelegate respondsToSelector:#selector(getButtonTitile:)])
{
[self.customDelegate getButtonTitile:button.currentTitle];
}
#MasterViewController.m
create obj of DetailViewController
DetailViewController *obj = [[DetailViewController alloc] init];
obj.customDelegate = self;
[self.navigationController pushViewController:reportTypeVC animated:YES];
and add delegate method in MasterViewController.m for get button title.
#pragma mark -
#pragma mark - Custom Delegate Method
-(void) getButtonTitile:(NSString *)btnTitle;
{
NSLog(#"%#", btnTitle);
}
I want to override an NSString property declared in a superclass. When I try to do it using the default ivar, which uses the the same name as the property but with an underscore, it's not recognised as a variable name. It looks something like this...
The interface of the superclass(I don't implement the getter or setter in this class):
//Animal.h
#interface Animal : NSObject
#property (strong, nonatomic) NSString *species;
#end
The implementation in the subclass:
//Human.m
#implementation
- (NSString *)species
{
//This is what I want to work but it doesn't and I don't know why
if(!_species) _species = #"Homo sapiens";
return _species;
}
#end
Only the superclass has access to the ivar _species. Your subclass should look like this:
- (NSString *)species {
NSString *value = [super species];
if (!value) {
self.species = #"Homo sapiens";
}
return [super species];
}
That sets the value to a default if it isn't currently set at all. Another option would be:
- (NSString *)species {
NSString *result = [super species];
if (!result) {
result = #"Home sapiens";
}
return result;
}
This doesn't update the value if there is no value. It simply returns a default as needed.
to access the superclass variables, they must be marked as #protected, access to such variables will be only inside the class and its heirs
#interface ObjectA : NSObject
{
#protected NSObject *_myProperty;
}
#property (nonatomic, strong, readonly) NSObject *myProperty;
#end
#interface ObjectB : ObjectA
#end
#implementation ObjectA
#synthesize myProperty = _myProperty;
#end
#implementation ObjectB
- (id)init
{
self = [super init];
if (self){
_myProperty = [NSObject new];
}
return self;
}
#end
I'm making elevator thing. I'm having trouble sending data with different views using presentModalViewController. I got red message "favoriteColorString" property not found. I copied exactly the same but different form names and buttons. The "favoriteColorString" appears an error and unable to send elevator2 data.
I tried two different thing.
Elevator2View.favoriteColorString = [[NSString alloc] initWithFormat:#"Your favorite color is %#", favoriteColorTextField.text];
And
favoriteColorString = [[NSString alloc] initWithFormat:#"Your favorite color is %#", favoriteColorTextField.text];
Here's my code:
ElevatorView.h
#import <UIKit/UIKit.h>
#import "Elevator2View.h"
#interface ElevatorView : UIViewController<PassSecondColor>
{
Elevator2View *Elevator2View;
IBOutlet UITextField *favoriteColorTextField;
IBOutlet UILabel *favoriteColorLabel;
IBOutlet UILabel *secondFavoriteColorLabel;
NSString *secondFavoriteColorString;
}
#property (nonatomic, retain) Elevator2View *Elevator2View;
#property (nonatomic, retain) IBOutlet UITextField *favoriteColorTextField;
#property (nonatomic, retain) IBOutlet UILabel *favoriteColorLabel;
#property (nonatomic, retain) IBOutlet UILabel *secondFavoriteColorLabel;
#property (copy) NSString *secondFavoriteColorString;
#end
ElevatorView.m
#import "ElevatorView.h"
#import "Elevator2View.h"
#implementation ElevatorView
#synthesize Elevator2View, favoriteColorTextField, favoriteColorLabel, secondFavoriteColorLabel;
#synthesize secondFavoriteColorString;
-(IBAction)level1:(id)sender;{
favoriteColorTextField.text = #"1";
Elevator2View.favoriteColorString = [[NSString alloc] initWithFormat:#"Your favorite color is %#", favoriteColorTextField.text];
[self presentModalViewController:[[[Elevator2View alloc] init]
autorelease] animated:NO];
}
Elevator2View.h
#import <UIKit/UIKit.h>
#protocol PassSecondColor <NSObject>
#required
- (void) setSecondFavoriteColor:(NSString *)secondFavoriteColor;
#end
#interface Elevator2View : UIViewController{
IBOutlet UITextField *secondFavoriteColorTextField;
IBOutlet UILabel *favoriteColorLabel;
IBOutlet UILabel *secondFavoriteColorLabel;
NSString *favoriteColorString;
id <PassSecondColor> delegate;
}
#property (copy) NSString *favoriteColorString;
#property (nonatomic, retain) IBOutlet UITextField *secondFavoriteColorTextField;
#property (nonatomic, retain) IBOutlet UILabel *favoriteColorLabel;
#property (nonatomic, retain) IBOutlet UILabel *secondFavoriteColorLabel;
#property (retain) id delegate;
#end
Elevator2View.m
#import "Elevator2View.h"
#interface Elevator2View ()
#end
#implementation Elevator2View
#synthesize secondFavoriteColorTextField, favoriteColorLabel, secondFavoriteColorLabel;
#synthesize favoriteColorString;
#synthesize delegate;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void) viewWillAppear:(BOOL)animated
{
favoriteColorLabel.text = favoriteColorString;
}
- (void) viewWillDisappear:(BOOL) animated
{
// [[self delegate] setSecondFavoriteColor:secondFavoriteColorTextField.text];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
favoriteColorLabel.text = favoriteColorString;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
See http://www.theappcodeblog.com/?p=90
The reason for your "property not found" is that you named your ivar same as class.
Dot notation is just a syntactic sugar: object.property = value is equivalent to [object setProperty:value]. In Objective C classes are also objects, and when you call Elevator2View.favoriteColorString = whatever, Xcode apparently thinks that you are attempting to call class method setFavoriteColorString of class Elevator2View.
Getting rid of this error is easy: just rename your ivar Elevator2View *Elevator2View to something else. In fact, Xcode 4.4 and newer autosynthesizes ivars for your properties: if you have a property propertyName, then Xcode will autosynthesize ivar _propertyName. Your property Elevator2View will have _Elevator2View ivar. So unless you really really need to have ivars with different naming scheme, you can get rid of your #synthesize, and you also don't need to declare ivars for you properties.
(Though I prefer to declare ivars for properties (following Xcode naming scheme), because far too often lldb doesn't show autosynthesized-without-declaring ivars in object inspector.)
That was about properties, ivars and naming conventions. But what are you doing in this code?
-(IBAction)level1:(id)sender;{
favoriteColorTextField.text = #"1";
Elevator2View.favoriteColorString = [[NSString alloc] initWithFormat:#"Your favorite color is %#", favoriteColorTextField.text];
[self presentModalViewController:[[[Elevator2View alloc] init]
autorelease] animated:NO];
}
You set value of Elevator2View's - your instance variable's - property, then create brand new object of Elevator2View class and present that as modal view controller. (By the way, presentModalViewController:animated: is deprecated in iOS 6.0). Of course, this brand new Elevator2View object has no idea what Elevator2View's (your instance variable's) properties are!