Is a particular synthesized property an oxymoron?

After going through an iPhone beginner’s developer’s book and reading a sample code online, I noticed that most Objective-C programmers synthesize almost every instance variable. Some variables are convenient for snythesize, but most of them should not adhere to the object-oriented principle of encapsulation. The worst synthesized properties are marked as private. A C ++ programmer trying to use another user's code will read public fields and methods in the header file. They will skip private variables. This C ++ programmer will not know that you plan to use private properties in any meaningful way.

Take a look at this sample template for the lazy table loading provided by Apple:

Headline

@interface ParseOperation : NSOperation <NSXMLParserDelegate> { @private id <ParseOperationDelegate> delegate; NSData *dataToParse; NSMutableArray *workingArray; AppRecord *workingEntry; NSMutableString *workingPropertyString; NSArray *elementsToParse; BOOL storingCharacterData; } 

A source

 @interface ParseOperation () @property (nonatomic, assign) id <ParseOperationDelegate> delegate; @property (nonatomic, retain) NSData *dataToParse; @property (nonatomic, retain) NSMutableArray *workingArray; @property (nonatomic, retain) AppRecord *workingEntry; @property (nonatomic, retain) NSMutableString *workingPropertyString; @property (nonatomic, retain) NSArray *elementsToParse; @property (nonatomic, assign) BOOL storingCharacterData; @end @implementation ParseOperation @synthesize delegate, dataToParse, workingArray, workingEntry, workingPropertyString, elementsToParse, storingCharacterData; 

Now I know that this is not C ++, and we should not assume that all C ++ practices should be performed in Objective C. But Objective-C must have good reasons to deviate from general programming methods.

  • Why are all synthetic ivars? When you look at the project as a whole, only NSMutableArray *workingArray used by external classes. Thus, none of the other Ivars should have setters or getters.
  • Why are very sensitive ivars synthesized? First, now that the id delegate has a setter, the user of this object can switch the delegate in the middle of the XML parsing, which makes no sense. In addition, NSData *dataToParse is raw XML data received from the network. Now that he has a setter, the user of this object can ruin the data.
  • What is the point of marking all private in the header? Since all Ivars are synthesized to have getters / setters, they are effectively published. You can install them on everything you want, and you can get their value whenever you want.
+8
oop objective-c encapsulation iphone
source share
2 answers

I follow the idiom modeled by this example in many of my classes, so I can try to explain my justification for this practice.

The properties in this example are declared in the class extension in the .m file. This makes them virtually private. Any attempt to access these properties from another class will result in a "Property not found" error during compilation.

For developers coming from other languages, it may seem strange to synthesize getters and setters for private instance variables. Indeed, there is only one reason why I do this. When used continuously, synthesized properties can simplify memory management and help avoid careless errors that can lead to errors. Here are some examples:

Consider this:

 self.workingPropertyString = [NSMutableString string]; 

in comparison with this:

 workingPropertyString = [[NSMutableString string] retain]; 

Many developers claim that these two assignments are functionally equivalent, but there is an important difference. The second purpose of a memory leak is if workPropertyString has already pointed to a stored object. To write code functionally equivalent to a synthesized network device, you need to do something like this:

 NSMutableString *newString = [NSMutableString string]; if (workingPropertyString != newString) { [workingPropertyString release]; workingPropertyString = [newString retain]; } 

This code avoids the leakage of any existing object that can be referenced by an instance variable, and safely handles the possibility of re-assigning the same object to an instance variable. A synthesized setter does it all for you.

Of course, we can see that (workingPropertyString != newString) will always be true in this case, so we could simplify this task. In fact, in most cases, you probably avoid the simple direct assignment of an instance variable, but of course, these are exceptional cases that tend to create most errors. I prefer to play it safely and set all the instance variables of the object through synthesized setters. All assignments of objects of my instance are simple single-line ones that look like this:

 self.foo = [Foo fooWithTitle:@"The Foo"]; 

or that:

 self.foo = [[[Foo alloc] initWithTitle:@"The Foo"] autorelease]; 

This simplicity and consistency gives my weak brain less material to think about. As a result, I have almost no memory management errors. (I know that the autorelease idiom autorelease theoretically consume excessive memory in a narrow loop, but I have yet to face this problem in practice. If I ever do, this is a simple case for optimization.)

Another thing that I like about this practice is that my dealloc methods look like this:

 - (void)dealloc { self.delegate = nil; self.dataToParse = nil; self.workingArray = nil; self.workingEntry = nil; self.workingPropertyString = nil; self.elementsToParse = nil; [super dealloc]; } 

EDIT: Daniel Dickison pointed out some risk of using accessories in dealloc that I did not consider. See comments.

where each property of the object is simply set to nil. This simultaneously frees each saved property when it is set to zero, in order to avoid certain crashes due to EXC_BAD_ACCESS.

Note that I set self.delegate = nil; although this property has been declared as (nonatomic, assign) . This appointment was not strictly necessary. In fact, I could completely get rid of the properties for my objects (nonatomic, assign) , but again I found that the constant use of this idiom in all my instance variables makes my brain think less and further reduces the chance that I will create an error through some careless mistake. If necessary, I can simply flip the property from (nonatomic, assign) to (nonatomic, retain) without having to touch any memory management code. I like it.

You can also use consistency as an argument to synthesize properties for private scalar variables, as your example did in the case of BOOL storingCharacterData; . This practice ensures that every assignment to an instance variable looks like self.foo = bar; . Usually I’m not used to creating private scalar properties myself, but I see some excuses for this practice.

+10
source share

Why is all private ivars synthesized? When you look at the project as a whole, only NSMutableArray * workingArray is used for external classes. Therefore, none of the other Ivars should have setters and miners.

No real need; if you still want to access all ivars, there is no need for @synthesize.

Why are very sensitive ivars synthesized? Firstly, now that the delegate id has a setter, the user can switch the delegate in the middle of the XML parsing, something does not make sense. In addition, NSData * dataToParse is raw XML data received from the network. Now that it has a setter, the user of this object may corrupt data.

Announced publicly not announced any of the participants / recipients. If the class client wanted to mess things up by switching the delegate in the middle, they would have to break the encapsulation to do this.

So, ultimately, not a problem.

What is the point of labeling everything closed in the title? Because all ivars are synthesized to have getters / setters, they are effectively public. You can install them in whatever you want, and you can get their value whenever you want.

Note that there is no need to even declare ivars in this example; the compiler automatically synthesizes them based on the @property declaration.

Traditionally, @private is protected from the fact that someone removes ivar directly from the outside into an instance of the class.

Note that anInstance-> ivar or self-> ivar is almost never used (and when used, it is almost always for the wrong reason). There is a use for this, but this is rare.

0
source share

All Articles